1. var reverse = function(head) {
if (head === null || head.next === null) {
return head;
}
var last = reverse(head.next);
head.next.next = head;
head.next = null;
return last;
};
if (head === null || head.next === null) {
return head;
}
head 指向链表第一个节点
递归终止的条件,只执行一次,head === null || head.next === null表示单链表最后一个节点
var last = reverse(head.next);
head.next.next = head;
head.next = null;
return last;
循环执行的逻辑
2.
var last = reverse(head.next);
这段是比较重点的,head 是当前的节点,先递归当前的节点的下一个节点,当前节点后续代码会延后执行
这里last 跟最后一句代码 return last;是对应的.可以看到last的值从未修改过,所以每层递归函数的
last都是一样。
先说下last是什么,last本来是指向最后一个节点,但是颠倒后就是第一个节点了,所以last 指向反转后
的链表
3.
前期代码执行的顺序是先递,
if (head === null || head.next === null) {
return head;
}
var last = reverse(head.next);
比如:链表 L = [1 |] => [2 |] => [3 |] => [4 |] => [5 |] => null ,递的结果是
归是 从 reverse(5)开始
if (head === null || head.next === null) {
return head;
}
var last = reverse(head.next);
第5个节点的next 的值为 null
所以进入了
if分支,唯一执行一次这段代码,因为是递归终止的条件。
所以reverse(5)return 5, 函数结束,并不会继续后续的代码
5.
reverse(4)
if (4 === null || 4.next === null) {
return 4;
}
var 5 = reverse(5);
是从这里开始执行的
head.next.next = head;
head.next = null;
return last; //这段返回的last 指针作为头指针,每个函数都是如此,所以不用再说了。
head.next.next = head;
head.next = null;
主要的归代码 就是这两句。
head.next 指的是当前节点的下一个节点, 即 节点 [5 |]
head.next.next 就是下一个节点的next指针 即 5.next
head.next 当前节点的next 即 4.next
所以这两段代码的意思就是
二、反转链表前 N 个节点
需要3个指针
var successor = null;
function reverseN(head, n) {
if (n == 1) {
// 记录第 n + 1 个节点
successor = head.next;
return head;
}
// 以 head.next 为起点,需要反转前 n - 1 个节点
var last = reverseN(head.next, n - 1);
head.next.next = head;
// 让反转之后的 head 节点和后面的节点连起来
head.next = successor;
return last;
}
当n == 1 时,已经是要反转的最后一个节点了。即递到了第n个元素,此时确定了successor
指针指向的节点,即第n + 1 个节点。然后返回 head 指针,head 指针指向的是当前的节点。
head 作为返回值 赋值 给n - 1 个节点 的last 变量。
var last = reverseN(head.next, n - 1);
...
return last;
这两段代码保证了后续的last 的值是不会改动,last 指向反转前的第n 个节点,
head.next.next = head;
这段代码也没有什么变化,作用还是一样的,下一个节点指向当前的节点。
head.next = successor;
唯一不同的就是这段代码,当前节点不是置空,而是指向successor指针,区别也不是很大,
这一步是为了保证代码执行到最后 反转前的第一个节点 指向 successor 。
不做这一步也没问题,因为真正需要这一步的就是最后一个归操作,但是n 无法确定,不知道最后一个递归的时机。