Список

Материал из Викиконспекты
Версия от 18:27, 11 июня 2014; 79.134.198.186 (обсуждение) (Задача на поиск цикла в списке)
Перейти к: навигация, поиск

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

Односвязный список

Простейшая реализация списка. В узлах хранятся данные и указатель на следующий элемент в списке.

SimpleSpisok.png

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

Также хранится указатель на предыдущий элемент списка, благодаря чему становится проще удалять и переставлять элементы.

TwiceSpisok.png

XOR-связный список

В некоторых случаях использование двусвязного списка в явном виде является нецелесообразным. В целях экономии памяти можно хранить только результат выполнения операции Xor над адресами предыдущего и следующего элементов списка. Таким образом, зная адрес предыдущего элемента, мы можем вычислить адрес следующего элемента.

Циклический список

Первый элемент является следующим для последнего элемента списка.

CircleSpisok.png

Операции на списке

Рассмотрим базовые операции на примере односвязного списка.

Вставка

Очевиден случай, когда необходимо добавить элемент ([math]newHead[/math]) в голову списка. Установим в этом элементе ссылку на старую голову, и обновим указатель на голову.

 function insert(newHead)
   newHead.next = head
   head = newHead 
InsertHead.png

Если же на нужно вставить элемент ([math]thatElement[/math]) в определенную позицию после какого-то другого элемента ([math]thisElement[/math]), то просто изменим соответствующие ссылки.

 function insertAfter(thisElement, thatElement)
   thatElement.next = thisElement.next 
   thisElement.next = thatElement
InsertAfter.png

Поиск

Для того, чтобы найти элемент по значению ([math]value[/math]), будем двигаться по списку от головы до конца и сравнивать значение в элементах с искомым. Если элемента в списке нет, то возвращаем [math]NULL[/math].

 Node Search(value)
   node = head
   while node != NULL and value != node.value
       node = node.next
   return node

Удаление

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

 function removeHead()
   if head != NULL
       tmp = head
       head = head.next
       delete tmp
RemoveHead.png

Удаление элемента после заданного ([math]thisElement[/math]) происходит следующим образом: изменим ссылку на следующий элемент на следующий за удаляемым, затем удалим нужный объект.

 function removeAfter(thisElement)
   if thisElement.next != NULL
       tmp = thisElement.next
       thisElement.next = thisElement.next.next
       delete tmp
RemoveAfter.png

Задача на поиск цикла в списке

Для начала необходимо уметь определять - список циклический или нет. Воспользуемся алгоритмом Флойда "Черепаха и заяц". Пусть за одну итерацию первый указатель(черепаха) переходит к следующему элементу списка, а второй указатель(заяц) на два элемента вперед. Тогда, если эти два указателя встретятся, то цикл найден, если дошли до конца списка, то цикла нет.

 boolean isCycle(head)
   firstElement = head.next
   secondElement = head.next.next
   while firstElement != secondElement
     if secondElement == NULL or secondElement.next == NULL 
       return false
     firstElement = firstElement.next
     secondElement = secondElement.next.next
   return true

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

 int getTail(head, pointMeeting)
   firstElement = head.next
   secondElement = pointMeeting.next
   lengthTail = 1
   while firstElement != secondElement
     firstElement = firstElement.next
     secondElement = secondElement.next
     lengthTail = lenghtTail + 1
   return lengthTail

См.также

Источники информации

  • Wikipedia — Linked list
  • Википедия — Список
  • Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд. — М.: «Вильямс», 2007. — Глава 11.2. — ISBN 5-8489-0857-4
  • Дональд Э. Кнут Искусство программирования. Том 1. Основные алгоритмы — 2-е изд. — М.: «Вильямс», 2012. — Глава 2.2. — ISBN 0-201-89685-0