Этот урок был изначально опубликован в https://algodaily.com Где я поддерживаю курс технического интервью и пишу думать-кусочки для амбициозных разработчиков.
Вы отправили Связанный список чисел, но он был получен в обратном порядке к тому, что вам нужно. Это произошло несколько раз сейчас, поэтому вы решите написать алгоритм, чтобы изменить список списков. Список, который вы получили, выглядит следующим образом:
// 17 -> 2 -> 21 -> 6 -> 42 -> 10
Напишите алгоритм для метода Реверсист который принимает в голова Узел в качестве параметра и обращает связуемый список. Это должно быть способно обратиться к списку любой длины.
Вы можете использовать пример Связанный список для целей тестирования. Ваш метод будет вызван как таковой:
class LinkedListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
l1 = new LinkedListNode(1);
l1.next = new LinkedListNode(2);
reverseList(l1);
Кажется довольно легко, верно? Чтобы изменить весь связанный список, просто обратьтесь каждый указатель. Если 1 указывает на 2 Переверните это так 2 должен указать на 1 Отказ
// 17 -> 2 -> 21 -> 6 -> 42 -> 10 // becomes // 17 <- 2 <- 21 <- 6 <- 42 <- 10
Фактический метод разворота на самом деле довольно прост, но осознавать, что требуется некоторое время для рассуждения. Легко терять, поэтому убедитесь, что вы рисуете много диаграмм.
Поскольку это проблема (обратный целый связанный список), которые могут быть разбиты в подпроставлениям (обратный указатель между двумя узлами), похоже, что хорошая возможность использовать рекурсию.
Есть много способов сделать фактическую разворот, и мы покроем как итерационный и рекурсивный Подход, но общая методология заключается в следующем:
Начните с создания 3 указателей:
Newhead,головаиNextNodeОтказNewheadиNextNodeинициализируются наnullОтказголоваЗапускается, указывая на главу связанного списка.
- Итерации (или рекурсивно делайте) через следующий процесс до
головаэтонулевой. Это означает, что конец списка был достигнут:
class LinkedListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
l1 = new LinkedListNode(1);
l2 = new LinkedListNode(2);
l1.next = l2;
// we start at head
let head = l1;
let newHead = null;
while (head != null) {
// store the node to the right to reuse later
let nextNode = head.next;
// set the current node's next to point backwards
head.next = newHead;
// store the current node, to be used as the new next later
newHead = head;
// the previously right-side node is now processed
head = nextNode;
}
console.log(l2);
Трудно визуализировать эту цепочку событий, поэтому давайте будем использовать комментарии для его визуализации. Во время интервью Старайтесь не держать его в своей голове .
Это будет особенно сложно, уравновешивая ваши нервы и разговаривая с интервьюером. Воспользуйтесь преимуществами доски, а не только для записи вещей, но и думать через потенциальные шаги.
Давайте пройдем через это шаг за шагом, а затем посмотрите на рабочий код. Давайте обратимся от чрезвычайно базового списка, как 8 -> 4 Отказ Первая строка – let.next; , который будет хранить узел справа.
nextNode = 4 // 8 -> 4
Тогда мы сделаем голова. , который установит текущий узел Следующий указывать назад.
nextNode = 4 // <- 8, 4
Сейчас Ньюхед; Будут хранить текущий узел, который будет использоваться в качестве нового следующего позже.
newHead = 8 nextNode = 4 // <- 8, 4
Наконец, ранее обрабатываемый правый узел теперь обрабатывается:
newHead = 8
nextNode = 4
// <- 8, 4
^
current node
Теперь мы обрабатываем следующий с одинаковыми шагами. NextNode.Next; хранит узел справа.
newHead = 8
nextNode = null
// <- 8, 4
^
current node
Опять же, установите текущий узел Следующий указывать на обратную сторону голова. Отказ Напомним, что Newhead это 8 Действительно Это где мы делаем переключатель:
newHead = 8
nextNode = null
// <- 8 <- 4
^
current node
Теперь давайте посмотрим на это все вместе в коде, с большим количеством комментариев для редактирования!
class LinkedListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
l1 = new LinkedListNode(8);
l2 = new LinkedListNode(4);
l1.next = l2;
// start at head, 8
let head = l1;
// example: 8 -> 4
let newHead = null;
while (head) {
/* FIRST PASS */
// store the node to the right
let nextNode = head.next;
// nextNode = 4, still 8 -> 4
// set the current node's next to point backwards
head.next = newHead;
// 8 -> null
// store the current node, to be used as the new next later
newHead = head;
// newHead = 8
// the previously right-side node is now processed
head = nextNode;
// head = 4
/* SECOND PASS */
// store the node to the right
nextNode = head.next;
// nextNode = null
// set the current node's next to point backwards
head.next = newHead;
// 4 -> 8
// store the current node as the previous one
newHead = head;
// the previously right-side node is now processed
head = nextNode;
}
console.log(l2);
Это все имеет смысл? Обязательно пройдите итеративный подход несколько раз.
Вот рекурсивный способ сделать это. Это также может быть сложно, особенно на первый взгляд, но осознайте большую часть волшебства происходит, когда доберется до конца.
function reverseList(head) {
if (!head || !head.next) {
return head;
}
let rest = reverseList(head.next);
head.next.next = head;
delete head.next;
return rest;
}
Давайте возьмем простой пример 8 -> 4 Опять Пусть (head.next); берет 4 и звонки Реверсист в теме.
Призыв Реверсист на 4 у нас достигнет пункта завершения, потому что нет .Next :
if (!head || !head.next) {
return head;
}
Мы идем вверх по стеке назад, когда 8 был обработан. отдых Теперь просто указывает на 4 Отказ Теперь обратите внимание, что происходит:
// remember, head is 8 - it is being processed // head.next is 4 head.next.next = head; // head.next.next was null since 4 wasn't pointing to anything // but now head.next (4) points to 8
И мы возвращаемся 4 – который указывает на 8 Отказ И мы можем просто экстраполировать, что для более длинных связанных списков! Обратите внимание, что рекурсивный подход требует больше места, потому что нам нужно поддерживать наш стек вызова.
Оригинал: “https://dev.to/jacobjzhang/a-visual-guide-to-reversing-a-linked-list-161e”