关于递归反转链表的思路
theme: channing-cyan
小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。
前言
算法一直是考验程序员编程思想的一个重要点,一般来说中小型公司在面试中都不会特意去问这种问题,一般都是借着面试者的回答来往下问,比如涉及到Glide就会问LRUCache,HashMap就会问存储和计算Hash的算法.最近我也看了下LeetCode的一些题目,就来讲解一下自己的思路吧
Code
首先来定义一下实体类
``` public class ListNode { int val = 0; ListNode next;
public ListNode(int data) {
val = data;
}
} ```
然后创建一个链表
```
public static void main(String[] args) {
ArrayList
for (ListNode listNode : listNodeList
) {
try {
System.out.println(listNode.toString());
} catch (Exception e) {
System.out.println(e.toString());
}
}
} ```
不用在意细节,只要我们目的达到了不就行了吗,你说对吗,宝贝
然后就是反转细节
public static ListNode rever(ListNode head) {
if (head.next == null) {
return head;
}
ListNode rever = rever(head.next);
head.next.next = head;
head.next = null;
return rever;
}
之前看一篇微信公众号的文章说自己不要代入递归,人的脑袋压不了几个栈,我觉得不应该这么说,即使数据无限大我们也应该压几个方法在脑袋里过一下,把整个流程和细节简单的形成一个印象,下面我用画图的方式来讲解一下我的思路
对了,递归的一个特性就是回溯
比如我们第一个rever
方法传递的参数是集合中第一个数据,也就是说他的基础val
是0,next
的ListNode的val
是1;如下图
不要看太后面,主要看我蓝色标注部分就行,你现在只要记住我的传递的数据就是那么多
if (head.next == null) {
return head;
}
因为链表的特性就是一个节点链接着另一个节点,上面代码就是判断当前节点是不是还有下一个节点,没有就直接返回了,这是递归的一个出口,不设置就无限递归直接报错了
ListNode rever = rever(head.next);
这一句就比较重要的,递归的起始点,将我们一开始传递进来的参数head
的next
作为参数再次调用自身.
我们一开始传递的ListNode
的val
是0,他的next
的ListNode的val
肯定是1.就是上图我蓝色标注的.所以说这个地方的参数ListNode
的 val
= 1.
走到这个地方之后就会进入新的方法,此时的我们rever
的参数head
他的val
是1了,next
指向的ListNode
的val
是2了.
再次判断传递进来的参数的next
是不是null,不是就继续往下走,再次走到
ListNode rever = rever(head.next);
再次进入新的方法,此时方法rever
的参数head
的val
是2了,next
指向的ListNode是null.这里我就压3个栈,压多了我直接脑袋冒烟,而且你们会越看越迷糊.
判断后直接return head
,也就是直接将当前参数返回过去了.
重点!!!此时递归出口已经触发,开始回溯,我现在将我们压的栈简单的写在下面
第一个栈
//head 的 val = 0,next 指向的 ListNode的 val = 1
public static ListNode rever(ListNode head) {
if (head.next == null) {
return head;
}
ListNode rever = rever(head.next);
head.next.next = head;
head.next = null;
return rever;
}
第二个栈
//head 的 val = 1,next 指向的 ListNode 的 val = 2
public static ListNode rever(ListNode head) {
if (head.next == null) {
return head;
}
ListNode rever = rever(head.next);
head.next.next = head;
head.next = null;
return rever;
}
第三个栈
//head 的 val = 2,next 指向的 ListNode = null 直接返回
public static ListNode rever(ListNode head) {
if (head.next == null) {
return head;
}
ListNode rever = rever(head.next);
head.next.next = head;
head.next = null;
return rever;
}
就先压三个,理解一下运行流程
此时第三个栈参数head
的next
指向的ListNode是null,所以直接返回到第二个栈中,因为递归的回溯吗,要回去当时调用你的方法,就相当于搭积木
第三个栈不符合要求,直接return
了,我们就相当于消费了这个方法,就需要回到第二个栈中.此时注意这个一行
ListNode rever = rever(head.next);
这个时候已经来到了我们的第二个栈了,第二个栈的参数head
的 val = 1
,next
指向的 ListNode
的 val = 2
,所以返回的就是我们传递的next
数据:val = 2
,next = null
然后继续向下执行
head.next.next = head;
head.next = null;
return rever;
已知head
的next
指向的ListNode为: val = 2
,next = null
经过上面代码第一行的洗礼我们的ListNode发生了变化: val = 2
,next = head
现在数据变成了这个样子
但是我们要的可不是双向链表,所以执行第二行
head.next = null;
然后数据就变成了这样
哇!你成功反转了一个节点,此时我们将目光向第三行看齐
return rever;
这一句将我们反转后的数据return
了,也代表了第二个栈的消亡.
此时来到了第一个栈,return
的ListNode数据为val = 2
,next
指向的ListNode就是我们上图:val
= 1,next
= null.
我们接着看代码
ListNode rever = rever(head.next);
head.next.next = head;
head.next = null;
return rever;
当前rever
数据就是我刚刚说的,我们开始走第二行.
head.next.next = head;
记住,这是第一个栈了,我们当时说得,第一个栈的数据是ListNode中val
= 0,next
指向的ListNode中val
= 1,因为第二个栈中的反转,现在指向的ListNode为null
了;
head.next
等于下面这个
head.next.next
等于这个
也就是说,将这个null
重新变为我们的第一个数据
然后老套路,不能双向啊,宝贝,给他置空
head.next = null;
然后
return rever;
哇,三个节点的链表就给我们这么处理完了,来打印一下吧
好啦,这就是我对于递归反转链表的思路,有的人第一次看可能很蒙蔽,卧槽,head.next.next,这特么啥啊,别着急,我第一次看别人博客,文章的时候也是这样,黑人问号脸.jpg
好啦就这么多,有点头疼,磨蹭磨蹭就睡觉了,明天还有好多工作呢,┏(^0^)┛
- 学习Android的第十七天
- 这是一个吸猫文章的标题,甚至可以有两行这么多哦
- 学习Android的第四天
- 学习Android的第一天
- 含有边框的TextView-Android
- 电池-Android
- 倒计时封装-Android
- 标题和状态栏滑动渐变(2)-Android
- 标题和状态栏滑动渐变(1)-Android
- 关于选项卡三方库FlycoTabLayout的使用及修改
- 关于第三方库SmartTabLayout的一点小修改
- 关于递归反转链表的思路
- 搜索历史记录的实现-Android
- 感觉让人耳目一新的动画库Lottie
- Android-关于设备唯一ID的奇技淫巧
- 稍微巧妙的双模块联动-ViewPager
- 动画库NineOldAndroids实战自定义悬浮窗
- 关于项目中圆角及特殊圆角的实际使用问题
- 自定义TextView可控制Drawable大小