递归逆置的做法真的很巧妙,最好画图自己想一下
我们的目标是:实现一个递归函数,给定一条单链表的首结点,逆置这条单链表,并返回逆置后的首结点
这种比较复杂的递归往往都有步骤可循,我常用的思路就是先写伪代码,按照边界条件,执行操作,返回操作的步骤来思考。
当然,第一步就是假设我们有这么一个递归函数reverse,它的功能就是给定一条单链表的首结点,逆置这条单链表,并返回逆置后的首结点
然后我们就可以写边界条件了,注意下面是类python的伪代码
- 边界条件
if node==null or node.next==null:
return node
很显然,结点为空或者结点只有一个的情况我们就直接返回node就好
- 关键操作+处理特殊条件
这一步是最关键的,我们之前已经假设了拥有reverse函数,那么我们可以这样做
对于当前的node,我们先逆置node.next
tnode=reverse(node.next)
此时我们会获得以node.next为首结点逆置后的单链表
ok,然后我们画图,分析一下当前的状况
你有没有发现,reverse(node.next)
之后,node.next就是逆置后的链表尾结点了
现在我们需要,把node连接上这条逆置的单链表,你先想一下怎么做。
答案就是这一行代码
node.next.next=node
如果到这里没看懂,或者觉得很抽象不好理解,建议自己多画图分析一下,弄懂了再往下看,经过这一步操作之后,链表会变成这个样子
看起来还不错,但是我们发现,node和node.next之间的链接没有断开 于是我们执行这一条语句
node.next=null
但是有的同学可能会问,这一步是必要的吗?因为我返回递归的时候,node.next.next=node
不是会更改这个node指针吗?
这条语句的确会更改node指针,但是如果是递归到首结点呢?
很显然我们如果没有node.next=null
这条语句的话,那么这条逆置单链表最后一个结点的指针(node)是错的!,它本来应该指向null的!
因此,下面这条语句是必须的
node.next=null
很完美,现在我们得到一条以node为首结点,逆置后的单链表了
- 返回条件
别忘了我们的目标,返回逆置链表的首结点
我们如何才能得到首结点呢?
我们应该想到,对一条链表来说,逆置后的首结点就是逆置之前的尾结点。但是如何在这个递归函数里面返回这个尾结点呢?先回顾一下之前的代码
到这里,我们已经有了如下的代码
def reverse(node):
if node==null or node.next==null:
return node
tnode=reverse(node.next)
node.next.next=node
node.next=null
我们先在草稿纸上面运行一下代码,发现,无论如何,在reverse(node.next)
这行代码里面,node会一直传入node.next进去,循环往复,我们会得到什么? 没错,if node==null or node.next==null
这个返回条件里面,我们最终会得到 当前链表的尾结点
到最后一个结点的时候,此时tnode就是当前链表的尾结点了,而node就是tnode的前一个结点
那我们应该返回tnode了
return tnode
到此为止,完成的代码如下:
def reverse(node):
if node==null or node.next==null:
return node
tnode=reverse(node.next)
node.next.next=node
node.next=null
return tnode
接下来我们看看执行代码会发生什么
我们执行
node.next.next=node
node.next=null
此时会发生什么?
看出来了吗?此时tnode=>node 已经被逆置了,这个时候会返回上一层,继续操作
以此类推,最终我们会得到
然后函数最终会返回tnode,而tnode始终都是最后一个结点,是没有改变的!!不信你可以在纸上模拟一下递归
最终代码如下
def reverse(node):
if node==null or node.next==null:
return node
tnode=reverse(node.next)
node.next.next=node
node.next=null
return tnode
分析到现在,你可能发现,我们并没有一开始就去涉及递归函数怎么做,而是假设已经存在了这么一个递归函数,然后按照以下顺序分析我们需要进行的操作
- 递归边界 一般考虑边界条件,例如传入空结点 or 只有一个结点的情况
- 关键操作 如果有多个结点,那么我应该做什么一般(common)操作使得算法能达到我想要的效果
- 通过特殊情况来分析一般情况(例如上面的尾结点node.next指针问题)
- 通过画图来模拟递归,考虑最后一层递归时应该做的操作
- 返回条件,明白自己的返回条件,例如这一题就是取得逆置链表的最后一个结点
leetcode上面有原题,可以自己试着做一下
https://leetcode-cn.com/problems/reverse-linked-list/submissions/
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if(head==None or head.next==None):
return head
first=self.reverseList(head.next)
head.next.next=head
head.next=None
return first