Jump to content

Двусвязный список

В информатике двусвязный список это связанная структура данных , состоящая из набора последовательно связанных записей, называемых узлами . Каждый узел содержит три поля : два поля ссылки ( ссылки на предыдущий и следующий узел в последовательности узлов) и одно поле данных. и конечного узлов ссылки начального Предыдущая и следующая , соответственно, указывают на какой-то терминатор, обычно сторожевой узел или null , чтобы облегчить обход списка. Если существует только один дозорный узел, то список циклически связан через дозорный узел. Его можно представить как два односвязных списка, сформированных из одних и тех же элементов данных, но в противоположном последовательном порядке.

Двусвязный список, узлы которого содержат три поля: целочисленное значение, ссылку на следующий узел и ссылку на предыдущий узел.
Двусвязный список, узлы которого содержат три поля: ссылку на предыдущий узел, целочисленное значение и ссылку на следующий узел.

Две связи узла позволяют перемещаться по списку в любом направлении. Хотя добавление или удаление узла в двусвязном списке требует изменения большего количества связей, чем те же операции в односвязном списке, эти операции проще и потенциально более эффективны (для узлов, отличных от первых узлов), поскольку нет необходимости отслеживать предыдущий узел во время обхода или нет необходимости просматривать список, чтобы найти предыдущий узел, чтобы его ссылку можно было изменить.

Номенклатура и реализация

[ редактировать ]

Первый и последний узлы двусвязного списка для всех практических приложений доступны немедленно (т. е. доступны без обхода и обычно называются головой и хвостом ) и, следовательно, позволяют обход списка с начала или конца списка соответственно: например , обход списка от начала до конца или от конца к началу при поиске в списке узла с определенным значением данных. Любой узел двусвязного списка, однажды полученный, можно использовать для начала нового обхода списка в любом направлении (к началу или концу) от данного узла.

Поля ссылок узла двусвязного списка часто называются next и previous или вперед и назад . Ссылки, хранящиеся в полях ссылок, обычно реализуются как указатели , но (как и в любой связанной структуре данных) они также могут быть смещениями адресов или индексами в массиве , в котором находятся узлы.

Основные алгоритмы

[ редактировать ]

Рассмотрим следующие базовые алгоритмы, написанные на языке Ada:

Открытие двусвязных списков

[ редактировать ]
record DoublyLinkedNode {
    next // A reference to the next node
    prev // A reference to the previous node
    data // Data or a reference to data
}
record DoublyLinkedList {
     DoublyLinkedNode firstNode   // points to first node of list
     DoublyLinkedNode lastNode    // points to last node of list
}

Обход списка

[ редактировать ]

Обход двусвязного списка может осуществляться в любом направлении. На самом деле, при желании направление обхода можно менять много раз. Обход часто называют итерацией , но такой выбор терминологии неудачен, поскольку итерация имеет четко определенную семантику (например, в математике), которая не аналогична обходу .

Нападающие

node  := list.firstNode
while node ≠ null
    <do something with node.data>
    node  := node.next

Назад

node  := list.lastNode
while node ≠ null
    <do something with node.data>
    node  := node.prev

Вставка узла

[ редактировать ]

Эти симметричные функции вставляют узел после или перед данным узлом:

function insertAfter(List list, Node node, Node newNode)
    newNode.prev  := node      
    if node.next == null
        newNode.next  := null -- (not always necessary)
        list.lastNode  := newNode
    else
        newNode.next  := node.next
        node.next.prev  := newNode
    node.next  := newNode
function insertBefore(List list, Node node, Node newNode)
    newNode.next  := node
    if node.prev == null
        newNode.prev  := null -- (not always necessary)
        list.firstNode  := newNode
    else
        newNode.prev  := node.prev
        node.prev.next  := newNode
    node.prev  := newNode

Нам также нужна функция для вставки узла в начало возможно пустого списка:

function insertBeginning(List list, Node newNode)
    if list.firstNode == null
        list.firstNode  := newNode
        list.lastNode   := newNode
        newNode.prev  := null
        newNode.next  := null
    else
        insertBefore(list, list.firstNode, newNode)

Симметричная функция вставляется в конец:

function insertEnd(List list, Node newNode)
     if list.lastNode == null
         insertBeginning(list, newNode)
     else
         insertAfter(list, list.lastNode, newNode)

Удаление узла

[ редактировать ]

Удаление узла проще, чем вставка, но требует специальной обработки, если удаляемый узел является firstNode или LastNode :

function remove(List list, Node node)
    if node.prev == null
        list.firstNode  := node.next
    else
        node.prev.next  := node.next
    if node.next == null
        list.lastNode  := node.prev
    else
        node.next.prev  := node.prev

Одним из тонких последствий описанной выше процедуры является то, что удаление последнего узла списка устанавливает для firstNode и LastNode значение null , и поэтому удаление последнего узла из одноэлементного списка выполняется правильно. Обратите внимание, что нам также не нужны отдельные методы «removeBefore» или «removeAfter», поскольку в двусвязном списке мы можем просто использовать «remove(node.prev)» или «remove(node.next)», если они допустимы. Это также предполагает, что удаляемый узел гарантированно существует. Если узел не существует в этом списке, потребуется некоторая обработка ошибок.

Круговые двусвязные списки

[ редактировать ]

Обход списка

[ редактировать ]

Предполагая, что someNode — это некоторый узел в непустом списке, этот код проходит через этот список, начиная с someNode (подойдет любой узел):

Нападающие

node  := someNode
do
    do something with node.value
    node  := node.next
while node ≠ someNode

Назад

node  := someNode
do
    do something with node.value
    node  := node.prev
while node ≠ someNode

Обратите внимание на отсрочку теста до конца цикла. Это важно для случая, когда список содержит только один узел someNode .

Вставка узла

[ редактировать ]

Эта простая функция вставляет узел в двусвязный циклически связанный список после заданного элемента:

function insertAfter(Node node, Node newNode)
    newNode.next  := node.next
    newNode.prev  := node
    node.next.prev  := newNode
    node.next       := newNode

Чтобы выполнить «insertBefore», мы можем просто «insertAfter(node.prev, newNode)».

Для вставки элемента в возможно пустой список требуется специальная функция:

function insertEnd(List list, Node node)
    if list.lastNode == null
        node.prev := node
        node.next := node
    else
        insertAfter(list.lastNode, node)
    list.lastNode := node

Для вставки в начале мы просто «insertAfter(list.lastNode, node)».

Наконец, удаление узла должно учитывать случай, когда список пуст:

function remove(List list, Node node);
    if node.next == node
        list.lastNode := null
    else
        node.next.prev := node.prev
        node.prev.next := node.next
        if node == list.lastNode
            list.lastNode := node.prev;
    destroy node

Удаление узла

[ редактировать ]

Как и в двусвязных списках, «removeAfter» и «removeBefore» можно реализовать с помощью «remove(list, node.prev)» и «remove(list, node.next)».

Расширенные концепции

[ редактировать ]

Асимметричный двусвязный список

[ редактировать ]

Асимметричный двусвязный список находится где-то между односвязным списком и обычным двусвязным списком. Он разделяет некоторые функции с односвязным списком (однонаправленный обход), а другие - с двусвязным списком (простота модификации).

Это список, в котором предыдущая ссылка каждого узла указывает не на предыдущий узел, а на ссылку на самого себя. Хотя это не имеет большого значения между узлами (оно просто указывает на смещение внутри предыдущего узла), оно меняет заголовок списка: это позволяет первому узлу firstNode . легко изменять ссылку [1] [2]

Пока узел находится в списке, его предыдущая ссылка никогда не равна нулю.

Вставка узла

[ редактировать ]

Чтобы вставить узел перед другим, мы меняем ссылку, указывающую на старый узел, используя ссылку prev ; ссылку нового узла затем установите следующую этого узла так, чтобы она указывала на старый узел, и соответствующим образом измените предыдущую ссылку .

function insertBefore(Node node, Node newNode)
    if node.prev == null
        error "The node is not in a list"
    newNode.prev  := node.prev
    atAddress(newNode.prev)  := newNode
    newNode.next  := node
    node.prev = addressOf(newNode.next)
function insertAfter(Node node, Node newNode)
    newNode.next  := node.next
    if newNode.next != null
        newNode.next.prev = addressOf(newNode.next)
    node.next  := newNode
    newNode.prev  := addressOf(node.next)

Удаление узла

[ редактировать ]

Чтобы удалить узел, мы просто изменяем ссылку, на которую указывает prev , независимо от того, был ли узел первым в списке.

function remove(Node node)
    atAddress(node.prev)  := node.next
    if node.next != null
        node.next.prev = node.prev
    destroy node

См. также

[ редактировать ]
  1. ^ «Предотвращение сбоев игры, связанных со связанными списками» . 9 сентября 2012 г.
  2. ^ «Кохо, за «Кодекс чести» » . Гитхаб . 20 апреля 2022 г.
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: f24ef16bb82e62b20db334025d175cce__1667436180
URL1:https://arc.ask3.ru/arc/aa/f2/ce/f24ef16bb82e62b20db334025d175cce.html
Заголовок, (Title) документа по адресу, URL1:
Doubly linked list - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)