一、 反转整个链表
LeetCode 206 :https://leetcode-cn.com/problems/reverse-linked-list/
① 递归实现
参考:公众号labuladong
箭头所指,即坑所在,报错信息,因为 空 链表没有 next 指针,所以,head == null
这个判断条件必须要在 head.next == null
前面。
改为:
var reverseList = function(head) {
if(head == null || head.next == null){
return head
}
const last = reverseList(head.next)
head.next.next = head
head.next = null
return last
};
思想非常之奇妙,相当于我执行了 reverseList
函数之后,我返回一个 原本 指向最结尾的指针,此时除了头节点以外,全都反过来了。
最后,处理一下头结点那部分的情况,返回 新的头指针(原先最后一位,现头指针) 即可。
总结:
base case
语句很关键,要考虑好,不然会出现上面的报错情况- 不要跳进递归,正如公众号文章所说,你的脑袋能压几个栈?
- 核心,相信函数定义,处理函数不能处理的情况即可
②迭代实现
先训练递归思想,迭代后面再说
二、反转部分链表
1. 反转前 n 个
与第一题的不同:
- base case 不同,变成了 n == 1
- head 不一定最后变成最后一个节点,所以要设置一个节点(称为后驱节点),它是第
n + 1
个节点,用来反转后,head.next = successor
,将整个链表连接起来。
let successor = null
function reverseNList(head, n){
if(n === 1){
successor = head.next
return head
}
let last = reverseNList(head.next, n - 1)
head.next.next = head
head.next = successor
return last
}
2. 反转(m,n)个节点
LeetCode 92 https://leetcode-cn.com/problems/reverse-linked-list-ii/
思想,将问题由繁琐,转换成一般。
那反转链表的前 n 个 是非常简单的,我们如何做到呢?
对于 head
我们是从第 m
个元素开始反转,反转结束的位置是 n
,那对于 head.next
,我们从第 m - 1
个元素开始反转,反转结束的位置是 n - 1
。对于 head.next.next.....
迟早有一步,我们当前指针相当于第1个元素开始反转,反转 x 个元素。
所以,根据这个思想
let successor = null
function reverseNList(head, n){
if(n === 1){
successor = head.next
return head
}
const last = reverseNList(head.next, n - 1)
head.next.next = head
head.next = successor
return last
}
var reverseBetween = function(head, left, right) {
if(left === 1){
return reverseNList(head, right)
}
const last = reverseBetween(head.next, left - 1, right - 1)
head.next = last
return head
};
结束。
问题:
- 为什么最后返回的是head?
不要陷入递归,这么想,我们将部分反转过来之后,返回的指针,是原先 第 n
个位置指针,此时 head
是 第 m - 1
个指针,这是需要将他们连接起来,因此 head.next = last
,至于 第 m - 2
个指针,人家的 next
本来就指向第 m - 1
呀。
三、递归总结
所以,不要考虑这些七七八八的,你只需要想:
- 写一个 base case
- 调用函数完成工作
- 需要完成函数没有完成的工作(善后)
- 返回值