Сегодня мы собираемся реализовать (отдельно) Связанный список данных Структура в JavaScript 🥳
Что такое связанный список?
В условиях Layman связанный список представляет собой список узлов, где каждый узел удерживает две вещи:
- Ценность
- Указатель на следующий узел в списке.
Первый узел списка называется «голова» Отказ Последний узел списка называется «хвост» Отказ
Голова и хвост не являются отдельными узлами. Они просто ссылки на первый и последний узел в списке, соответственно.
Последний узел списка указывает на null
Отказ Если список пусто, то оба голова и хвостик Укажите null
Отказ
Создание связанного списка в JavaScript
Чтобы определить наш связанный список данных списка, мы собираемся создать класс
Отказ Мы инициализируем голова
и хвост
ссылки как нулевой
. Мы также определяем некоторые методы ( Добавить
, Prepend
, Удалить
С GetFirst
, GetLast
и Toarray
).
class LinkedList { constructor() { this.head = null; this.tail = null; } append() {} prepend() {} delete() {} getFirst() {} getLast() {} toArray() {} }
Присоединиться к
Добавляет узел до конца списка.
Мы создаем новый объект узла с 2 свойствами:
- А
ценность
Имущество, содержащее предоставленную стоимость. - А
Следующий
Свойство, указывая на следующий узел, который в этом случае являетсяnull
, так как мы добавляем (это последний узел в списке).
Затем мы проверяем, есть ли хвост
Узел и обновите его, чтобы указать на недавно добавленную. Мы реализуем Если
Проверьте, потому что в случае пустого списка (при добавлении первого узла), то хвост
есть и должно быть нулевой
Поскольку следующий узел нет следующего узла.
После этого мы Установите вновь добавленный узел как хвост
Отказ
Наконец, мы проверяем, если голова
Ссылка нулевой
, который он должен сначала создавать список и Установите вновь добавленный узел как голова
.
И там у вас есть, метод добавления новых узлов на наш связанный список данных списка!
append(value) { // New node object const newNode = { value, next: null }; // Updating the previous tail node (last node) to point to the newly appended node if (this.tail) { this.tail.next = newNode; } // Setting the newly appended node as tail this.tail = newNode; // Setting the newly appended node as head, only if there is no head if (!this.head) { this.head = newNode; } }
Предшествовать
Добавьте узел к началу списка.
Этот метод, как и предыдущий, собирается принять ценность для добавления. Реализация проста:
Мы создаем новый объект узла с предоставленным значением, но вместо настройки Следующий
недвижимость в null
как в Добавить
Метод, Мы устанавливаем его, чтобы указать на текущий первый узел ( Глава
).
Тогда мы Установите новый узел как голова
, так как он сейчас сидит в начале нашего списка и, наконец, мы проверяем, указывает ли хвост на нулевой
Как мы сделали раньше, так что мы Установите его, чтобы указать на новый узел (В случае, если мы используем PROTEND, чтобы добавить узел в пустой список).
prepend(value) { // New node object, with pointing to the current head const newNode = { value, next: this.head, }; // Setting the newly prepended node as head this.head = newNode; // Setting the newly appended node as tail, only if there is no tail if (!this.tail) { this.tail = newNode; } }
Довольно простой, верно? Давайте теперь посмотрим на то, как мы могли бы реализовать Удалить
метод.
Удалить
Удаляет все узлы с определенным значением
Это будет тад более сложный. Но оставайся со мной, когда вы владеете основной логикой, вы собираетесь добиться более глубокого понимания на том, насколько связаны списки:)
Логика следующая:
- Если нет
голова
просто вернитеnull
, так как список пуст. - Создайте текущий ссылку узла, инициализированной с
голова
узел. - Структура через все узлы в списке найдите те, которые содержат определенное значение и «Удалить» их, указывая свой предыдущий индекс узла на следующий узел , нравится:
- В конце каждой петли увеличивайте текущий узел ссылки, установив его к следующему.
- Специальная обработка для
голова
ихвост
Узел удаления, как обсуждалось ниже.
Сначала мы собираемся обрабатывать два случая: пустой список и голова
Удаление. Мы удаляем голова
Узел, просто установив следующий узел как голова
.
ЗАМЕТКА: А в то время как
Цикл используется, потому что нам нужно найти и удалять все элементы, содержащие определенное значение, а не только первое представление. В случае, если мы удалим голова
и новый голова
Также квалифицируется для удаления, мы также должны удалить это тоже. То же самое, конечно, применяется для каждого последовательного узла, поэтому A в то время как
петля помогает нам с этим.
delete(value) { // If head is missing, the list is empty, therefore return null if (!this.head) { return null; } // Update head, by setting current head value to the next node while (this.head && this.head.value === value) { this.head.next; } }
Логика Nelection Core следующая:
- Петля через все узлы до тех пор, пока их
Следующий
Недвижимость неnull
(Что произойдет, когда мы пересекаемхвост
). - Если следующий узел квалифицируется для удаления, Установите текущий узел, чтобы указать на узел после следующего узла.
- В любом другом случае просто увеличивайте текущий узел, просто переназнавая его на следующий узел.
// Initializing currentNode reference as current head let currentNode = this.head; // Loop through every node that points to another one while (currentNode.next) { // Checking whether next node's value qualifies for deletion if (currentNode.next.value === value) { // Set current node's next property to next node's next property, // effectively leaving no node pointing to the next node (node in the 'middle') currentNode.next = currentNode.next.next; } // If node doesn't qualify for deletion, set current node to the next one else { currentNode = currentNode.next; } }
Наконец обрабатывать хвост
Узел Удаление, как это:
// Update tail, by setting current tail value to the last non-deleted node if (this.tail.value === value) { this.tail = currentNode; }
Наш код удаления теперь должен выглядеть так:
delete(value) { // If head is missing, the list is empty, therefore return null if (!this.head) { return null; } // Update head, by setting current head value to the next node while (this.head && this.head.value === value) { this.head.next; } // Initializing currentNode reference as current head let currentNode = this.head; // Loop through every node that points to another one while (currentNode.next) { // Checking whether next node's value qualifies for deletion if (currentNode.next.value === value) { // Set current node's next property to next node's next property, // effectively leaving no node pointing to the next node (node in the 'middle') currentNode.next = currentNode.next.next; } // If node doesn't qualify for deletion, set current node to the next one else { currentNode = currentNode.next; } } // Update tail, by setting current tail value to the last non-deleted node if (this.tail.value === value) { this.tail = currentNode; } }
Розетки
Возврат текущей головы или хвостовой узел
Эти методы тривиальные, просто возвращают текущий голова
и хвост
:
getFirst() { return this.head; } getLast() { return this.tail; }
тормить
Преобразует список в массив
Наконец, этот метод собирается преобразовать наш список на Массив
для целей визуализации 😎
Мы по сути собираемся подтолкнуть каждый узел Объект
к Массив
и вернуть его. Я надеюсь, что код самостоятельно объясняет:
toArray() { const elements = []; let currentNode = this.head; while (currentNode) { elements.push(currentNode); currentNode = currentNode.next; } return elements; }
Окончательный код
Это наш последний LinkedList класс
:
class LinkedList { constructor() { this.head = null; this.tail = null; } append(value) { // New node object const newNode = { value, next: null }; // Updating the previous tail node (last node) to point to the newly appended node if (this.tail) { this.tail.next = newNode; } // Setting the newly appended node as tail this.tail = newNode; // Setting the newly appended node as head, only if there is no head if (!this.head) { this.head = newNode; } } prepend(value) { // New node object, with pointing to the current head const newNode = { value, next: this.head }; // Setting the newly prepended node as head this.head = newNode; // Setting the newly appended node as tail, only if there is no tail if (!this.tail) { this.tail = newNode; } } delete(value) { // If head is missing, the list is empty, therefore return null if (!this.head) { return null; } // Update head, by setting current head value to the next node while (this.head && this.head.value === value) { this.head.next; } // Initializing currentNode reference as current head let currentNode = this.head; // Loop through every node that points to another one while (currentNode.next) { // Checking whether next node's value qualifies for deletion if (currentNode.next.value === value) { // Set current node's next property to next node's next property, // effectively leaving no node pointing to the next node (node in the 'middle') currentNode.next = currentNode.next.next; } // If node doesn't qualify for deletion, set current node to the next one else { currentNode = currentNode.next; } } // Update tail, by setting current tail value to the last non-deleted node if (this.tail.value === value) { this.tail = currentNode; } } getFirst() { return this.head; } getLast() { return this.tail; } toArray() { const elements = []; let currentNode = this.head; while (currentNode) { elements.push(currentNode); currentNode = currentNode.next; } return elements; } }
Связанный список против массива
Плюс
- При использовании связанного списка нет необходимости заранее указывать его длину, что является случай при работе с массивами. Это происходит, потому что массивы нуждаются в непрерывной области выделенной памяти, тогда как связанные списки не страдают от этого ограничения, просто потому, что они используют указатели к определению своих узлов.
ЗАМЕТКА: Однако JavaScript и большинство современных языков программирования реализуют абстрактный тип данных, называемый «динамические массивы». По сути, они автоматически автоматически изменяются массивы, которые позволяют нам эффективно использовать их во время записи кода на более высоком уровне. Под капотом основанный в основе JavaScript Engine создает «реальный» массив, который обычно увеличивается в размере и вмещает все наши ценности. Когда он полностью получен, создается новый, и все старые элементы копируются над ним.
- Другой случай использования, когда связанные списки сияют, когда мы часто добавляем новые элементы в начале или в любом месте, за исключением его конца. При использовании массивов вы должны сдвинуть все элементы вправо, чтобы предписать или информировать новый, поэтому тратить множество вычислений При использовании связанного списка вам просто нужно изменить указатель предыдущего узла, чтобы указать на новый узел.
Господин
- Доступ к элементам в LL представляет собой o (n) сложности времени (линейных), тогда как доступ к элементам в массиве составляет O (1) сложности времени (константы), при условии, что мы знаем индекс элемента, который мы пытаемся получить доступ, конечно. Это происходит потому, что в случае, если мы должны пройти каждый узел, пока мы не найдем тот, который мы ищем.
Спасибо за чтение, надеюсь, я был достаточно ясен. Не стесняйтесь предоставлять отзыв на случай, если я что-то пропустил! 😊
Оригинал: “https://dev.to/mliakos/creating-a-linked-list-in-javascript-dgh”