「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
今天学习的是翻转链表相关的练习,本文一共有三道翻转链表的题目,由浅至深,和大家一起探讨遇到链表翻转问题的思路,如有问题,希望批评指正。
第一题是简单题,单纯的翻转链表。
206. 反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
这道题难度不大,只需要将当前节点指向前一个节点即可,由于节点无法直接引用前一个节点,所以我们需要提前创建一个空间,暂存前一个节点,更改引用,并且暂存其后一个节点,再向前引用
这是第一种迭代方法:
var reverseList = function (head) {
// 创建额外空间
let prev = null;
// 遍历链表
while (head) {
// 暂存有一个节点
let next = head.next;
// 当前节点向前指引
head.next = prev;
// 当前节点赋值给新空间
prev = head;
// 当前节点指向下一个结点
head = next
}
// 循环完毕,返回新空间第一个节点
return prev
};
如上题中,1→2→3→4→5,while中第一次循环
next = 2→3→4→5,head = 1→null,prev = 1→null,head = 2→3→4→5
第二次循环
next = 3→4→5,head = 2→prev,prev = head = 2→1→null,head = 3→4→5
...
最后prev = 5→4→3→2→1。
一般情况下,如果有迭代方法,对应的就是递归方法,递归方法相比于迭代方法,就稍显复杂。递归的关键在于反向,从最后一个节点,向前查找,向前翻转。
n1->n2->n3->...->nk->n(k+1)->...->nm->null;
假定n(k+1)->...->nm已经反转
n1->n2->n3->...->nk->n(k+1)<-...<-nm;
假定n(k+1)->...->nm已经反转,此时需要将,n(k+1)->nk,
即,nk.next.next = nk,
但当翻转到n1时,我们需要将n1->null,否则将形成闭环。
var reverseList = function(head) {
// 当遍历到最后时,代表翻转结束
if (head == null || head.next == null) {
return head;
}
// 翻转遍历,直到找到最后一项开始反转
const newHead = reverseList(head.next);
// 将翻转的前一项指向自己
head.next.next = head;
// 自己指向空
head.next = null;
// 返回当前已翻转链表
return newHead;
};
原链表1→2→3→4→5
递归四次后,head = 5→null, return head
此时newHead = 5→null,head = 4→5→null ,head.next.next = head
⇒4→5→4,head.next = null ⇒4→null ⇒newHead ⇒ 5→4→null
返回第三次递归,此时 newHead ⇒ 5→4→null,
第三次递归的head ⇒ 3→4→null,因为此时3依旧指向4,4的指向已经变了
head.next.next = head⇒4→3,head.next = null⇒3→null,
由于newHead⇒5→4→null,4的指向发生变化,newHead⇒5→4→3→null
返回第二次递归...一次类推,最后,newHead ⇒5→4→3→2→1→null
下面是我的另一种解法,索性我就叫赋值法吧,就是每一次遍历都赋一个新值。
var reverseList = function (head) {
// 简历一个新的赋值空间
let link = null;
// 遍历head
while(head){
// 每一次都赋值一个新的空间,把当前head的val,赋值给新的val,
// 把上一个节点,转赋值给下一个结点
link = new ListNode(head.val,ans)
head = head.next
}
return link;
};
按照这个方法,
第一次循环:head ⇒2→3→4→5→null ,link⇒ 1→null
第二次循环:head ⇒3→4→5→null ,link ⇒ 2→1→null
...
直到head遍历完成,把val都赋值给了link。
上面就是链表反转的三种方法,如果有更多的解决方法,可以在留言区写出来。