Автор оригинала: FreeCodeCamp Community Member.
Кевин Тюреей
Как Стеки и очереди Связанные списки являются формой последовательной коллекции. Это не должно быть в порядке. Связанный список состоит из независимых узлов, которые могут содержать любой тип данных. Каждый узел имеет ссылку на следующий узел в ссылке.
Мы можем эмулировать стеки и очереди со связанными списками. Мы также можем использовать его в качестве базы для создания или увеличения других структур данных. С помощью связанных списков наши основные проблемы составляют Быстрые вставки и удаления, которые являются более эффективными для массивов.
Строительный блок этой структуры является узлом.
const Node = function(value) { this.value = value; this.next = null;};Наш узел построен с двумя свойствами, A ценность держать данные, а Следующий , ссылка изначально установлена на NULL. Следующий Свойство используется для «точки» на следующий узел в ссылке. Одним из недостатков связанных списков является то, что каждая ссылка требует большая накладная память, чем массив.
Реализация
const LinkedList = function(headvalue) { // !! coerces a value to a Boolean if (!!headvalue) { return "Must provide an initial value for the first node" } else { this._head = new Node(headvalue); this._tail = this.head; }};На нашем втором конструкторе мы тестируем на значение для обеспечения первого узла. Если true, мы продолжаем создать новый узел со значением, пройденным и устанавливаю голову в хвост изначально.
Вставка
LinkedList.prototype.insertAfter = function(node, value) { let newNode = new Node(value); let oldNext = node.next; newNode.next = oldNext; node.next = newNode; if (this._tail === node) { this._tail = newNode; } return newNode;};Для этого метода мы создаем новый узел и отрегулируйте ссылки. Предыдущая следующая ссылка оригинального узла теперь направлена на Newnode. Следующая ссылка Newnode «указана» к тому, к чему следует относиться к предыдущему узлу. Наконец, мы проверяем и сбросьте свойство хвоста.
LinkedList.prototype.insertHead = function(value) { let newHead = new Node(value); let oldHead = this._head newHead.next = oldHead; this._head = newHead; return this._head;};LinkedList.prototype.appendToTail = function(value) { let newTail = new Node(value); this._tail.next = newTail; this._tail = newTail; return this._tail;};Вставка в начале или конец связанного списка – это быстро, работает в постоянном времени. Для этого мы создаем новый узел со значением и переставьте наши ссылочные переменные. Мы сбрасываем узел, который сейчас является головой с inserthead или хвост с appendtotail Отказ
Эти операции представляют собой быстрые вставки для коллекций, толкают для стеков и enqueue для очередей. Может быть, это может прийти в голову, что беззифт для массивов одинаково. Нет, потому что с непримещением все члены коллекции должны быть перемещены один индекс. Это делает его линейной работой.
Удаление
LinkedList.prototype.removeAfter = function(node) { let removedNode = node.next; if (!!removedNode) { return "Nothing to remove" } else { let newNext = removedNode.next node.next = newNext; removedNode.next = null; // dereference to null to free up memory if (this._tail === removedNode) { this._tail = node; } } return removedNode;};Начиная с теста на узел для удаления, мы продолжаем регулировать ссылки. Дереференция Удаленный и устанавливая его на ноль важно. Это освобождает память и позволяет избежать нескольких ссылок на тот же объект.
LinkedList.prototype.removeHead = function() { let oldHead = this._head; let newHead = this._head.next; this._head = newHead; oldHead.next = null; return this._head;};Удаление головы и указанного узла в, Removaveter, являются постоянными во время удаления. Кроме того, если значение хвоста известно, то удаление хвоста можно сделать в O (1). Иначе мы должны двигаться линейно до конца, чтобы удалить его, O (n);
Петли и Foreach
Мы используем следующее для итерации через связанный список или для работы в каждом значении узла.
LinkedList.prototype.findNode = function(value) { let node = this._head; while(node) { if (node.value === value) { return node; } node = node.next; } return `No node with ${value} found`;};LinkedList.prototype.forEach = function(callback) { let node = this._head; while(node) { callback(node.value); node = node.next; }};LinkedList.prototype.print = function() { let results = []; this.forEach(function(value) { result.push(value); }); return result.join(', ');};Основным преимуществом связанных списков является быстрые вставки и удаления без переходных элементов или перераспределения пространства. Когда мы используем массив, пространство памяти смещается, что означает, что мы держим все это вместе. С помощью связанных списков мы можем иметь пространства памяти повсюду, нерешительное хранилище через использование ссылок. Для массивов, эта местность ссылок означает, что массивы имеют лучшую кэширование значений для более быстрого поиска. С соединенными списками кэширование не оптимизировано, и время доступа занимает дольше.
Другим аспектом связанных списков является разные типы конфигурации. Два основных примера являются круговидно Связанные, где хвост имеет ссылку на голову и голову к хвосту. Вдвойне Связанные это когда, в дополнение к узлу, имеющему ссылку на следующий узел, также имеет ссылку, оглядываясь назад на предыдущий узел.
Сложность времени
Вставка
- Inserthead, AppendTotail – O (1)
- Если конкретный узел известен, Ibsafter – O (1)
Удаление
- Удалить головы – O (1);
- Если конкретный узел известен, восстановление – O (1)
- Если узел не известен – o (n)
Прохождение
- Findnode, Foreach, Print – O (n)
Ресурсы
Местонаправленная ссылка Отличные ответы здесь А вот Связанный список
Спасибо за прочтение. Для практики попробуйте реализовать стек или очередь со связным списком или хранить массивы в каждом узле и извлечь данные. Спросите себя, когда я доберусь до массива, это, на самом деле, лучший выбор для моих потребностей?
Оригинал: “https://www.freecodecamp.org/news/data-structures-101-linked-lists-254c82cf5883/”