前言
前言:阅读《算法小抄》的实时记录,通过自己画图,自己敲一次代码来理解总结一波
《算法小抄》:递归反转链表
一、反转整个链表
代码
private class ListNode {
ListNode next;
Integer value;
}
/**
* 反转整链表
*
* @param head
* @return
*/
ListNode reverse(ListNode head) {
if (head.next == null) {
return head;
}
ListNode last = reverse(head.next);
// 递归完返回的就是指向最后一个节点
// 需要把原来的头节点处理成尾节点
head.next.next = head;
head.next = null;
//返回最后一个节点作为反转完的头节点.
return last;
}
解析
我们不能一步一步跳进递归来思考,可以拿一次来进入递归来思考
- 第一次递归的是head之后的链表
- 因为尾巴节点tail是NULL,所以可以拿NULL来作为递归函数的终止条件
- 假设我们成功递归完了,那最后一个跳出来递归的结果场景就是如图第二个链表所示
- head之后的链表已经反转了,并且返回last来标识,(last和head是敌对的)
- 我们这时候所需要做的就是把原来的head节点处理成最后一个节点,具体可以看代码。
- 返回last,就可以完成整个链表的反转啦
要是一些模糊的话可以把最后一次进递归的场景思考一遍,会更佳清晰了。
二、反转链表前 N 个节点
代码
// 原来需要递归的最后一个节点的next一定时null,但在这里不一定了,所以需要记录下来那个next节点避免丢失了
ListNode successor = null;
/**
* 反转链表前N个节点
* @param head
* @param n
* @return
*/
public ListNode reverseN(ListNode head, int n) {
//n为1 的时候就是剩余的链表就剩它自己一个节点了
if (n == 1) {
//此时的需要记录这个接班的节点
successor = head.next;
return head;
}
//递归剩余的节点
ListNode last = reverseN(head.next, n - 1);
//同样的要处理下头节点成尾节点
head.next.next = head;
head.next = successor;
return last;
}
解析
其实和反转整个链表的的代码很相似的,只是有一些点需要注意与修改
- 因为我们是反转前N个节点,除了head的next来递进之外,我们也需要N来做N-1来递进
反转整个链表的话,最后一个有效节点的next是NULL
,但在这里不是null了,所以需要特别记录当N等于1的next值。 - 并且最后处理尾节点的时候,head的next指向的应该是N=1的那个结点的next
三、反转链表区间节点
代码
/**
* 反转链表的区间
* 反转链表的M-N节点
* @param head
* @param m
* @param n
* @return
*/
public ListNode reverseBetween(ListNode head, int m, int n) {
if(m == 1) {
return reverseN(head.next, n);
}
head.next = reverseBetween(head.next,m - 1, n - 1);
return head;
}
// 原来需要递归的最后一个节点的next一定时null,但在这里不一定了,所以需要记录下来那个next节点避免丢失了
ListNode successor = null;
/**
* 反转链表前N个节点
* @param head
* @param n
* @return
*/
public ListNode reverseN(ListNode head, int n) {
//base case n为1 的时候就是剩余的链表就剩它自己一个节点了
if (n == 1) {
//此时的需要记录这个接班的节点
successor = head.next;
return head;
}
//递归剩余的节点
ListNode last = reverseN(head.next, n - 1);
//同样的要处理下头节点成尾节点
head.next.next = head;
head.next = successor;
return last;
}
解析
反转整个链表的区间节点其实又是一次升级了,我们从反转整个链表到反转前N个节点,一步一步闯关到这里。反转区间节点,当M为1的时候是不是就是反转前N个节点啦。那用递归的思想,思考下,不断拆解子任务,当把M-N子链作为一条链来看,是不是可以使用反转前N个节点的算法来解决。没错,我们要反转的也就是M-N子链。
- M不为1的时候就head、M、N都不断递进
- 当M为1的时候,触发反抓前N节点的算法
- 把原来的head的next指向返回来的last
总结
反转链表,可以说是递归的入门篇了。《算法小抄》的作者带我们一步一步使用递归来解决反转链表的变种问题。
在使用递归上因为我们普通人的脑子无法装得下那么深的栈,光用脑子想真的很容易碰壁,原来越复杂了。
还是要通过画图展现出最开始的场景和最后终止的场景,才能完美的解决。
其实解决链表问题上,递归的效率是低于循环迭代的,不过可以作为递归的思想训练啦~
很惭愧,大学的时候没有想过自己会往软件方面发展。在误打误撞从事了Java开发之后深知算法和数据结构的重要性。
无论在剖析java主流框架还是要在面试凸显而出都需要很深的算法功力。碰巧看到这本《算法小抄》书,虽然在网上的风评好像不太好,主要原因是作者存在抄袭嫌疑,但是内容本身是非常没问题的,整个网络也没有提到内容有问题。个人觉得对新手还是挺友好的,而且挺系统的。希望自己跟着刷完之后能对算法和数据结构的解题技巧和思路上有提升吧~