Python 这个递归函数如何反转链表?

Python 这个递归函数如何反转链表?,python,recursion,linked-list,Python,Recursion,Linked List,我发现下面的函数递归地反转链表: def recursive(self, head, end): if not head: return None, None if head.next == end: head.next = None return head, head newHead, newEnd = self.recursive(head.next, end) newEnd.next = head he

我发现下面的函数递归地反转链表:

def recursive(self, head, end):
    if not head:
        return None, None
    if head.next == end:
        head.next = None
        return head, head
    newHead, newEnd = self.recursive(head.next, end)
    newEnd.next = head
    head.next = None
    return newHead, head
我理解基本情况下的
if
语句

但我不明白这种循环关系

递归是如何反转列表的?有没有更简单的递归版本可以反转链表?作为参考,我正在解决LeetCode问题:

给定单链接列表的
标题
,反转列表,然后返回反转后的列表


实际上,我不理解这个函数。首先,您只需要将列表的头传递给这样一个函数;我不知道为什么要通过参数end,或者为什么需要通过它。此外,参数self表明这是一个方法,它是未显示的类定义的一部分。让我们重新开始:

下面是一个简单的类,用于定义一个节点,该节点可以链接到另一个节点,并用“名称”标记以进行标识。我们还包括一个小函数,用于遍历链接列表以打印节点:

类节点:
定义初始化(self,name):
self.name=名称
self.next=无
a=节点('a')
b=节点('b')
c=节点('c')
d=节点('d')
e=节点('e')
a、 下一个=b
b、 下一个=c
c、 next=d
d、 下一个=e
def打印列表(标题):
而负责人:
打印(标题名称)
head=head.next
打印列表(a)
印刷品:

A
B
C
D
E
Reversed:
E
D
C
B
A
接下来,我们介绍递归函数来反转这样的列表。此函数被传递给列表的head节点,断言为not
None
。其余的我在算法的每一步都试图解释为注释。一个关键点是评论7:让我们一直指向的东西现在指向我们。这与注释5有关:我们现在向后指向None,因为当我们设置
head.next=None
时,如果该节点是整个列表的head节点,它将只保留
None
。否则,根据注释7,稍后将对其进行更新,以指向前一个节点

def倒档(头部):
断言(头)#1。确保头不是空的
下一个头部=头部。下一个2。获取下一个节点(如果有)
如果next#u head为None:#3。如果我们不指向下一个节点,那么
返回头#4。只需将此节点作为新头返回即可
head.next=无#5。现在,我们向后指向“无”
# 6. 否则,递归地反转我们所指向的内容,得到新的头部
新头=反向(下一个头)
# 7. 让我们一直指向的东西现在指向我们:
下一个头
# 8. 并返回新的头
返回新的头
#没有明确的评论:
def reverse_no_注释(标题):
断言(头)
下一个头=头。下一个
如果下一个_头为无:
回流头
head.next=无
新头=反向(下一个头)
下一个头
返回新的头
def打印列表(标题):
而负责人:
打印(标题名称)
head=head.next
打印('反向:')
新头=反向(a)
打印列表(新打印头)
印刷品:

A
B
C
D
E
Reversed:
E
D
C
B
A
我不明白这种循环关系

假设我们有一个链表:

 head                                                  end
  ↓                                                     ↓
┌───────────┐    ┌───────────┐                        ┌───────────┐
│ value: 85 │    │ value: 15 │                        │ value: 20 │
│ next: ———————→ │ next: ———————→ ...more nodes...——→ │ next:null │
└───────────┘    └───────────┘                        └───────────┘ 
递归部分基于以下观察结果:

如果您可以反转一个短一个元素的列表,该列表不包括当前的
head
节点,那么我们应该遇到如下情况:

 head                                                  end
  ↓                                                     ↓
┌───────────┐    ┌───────────┐                        ┌───────────┐
│ value: 85 │    │ value: 15 │                        │ value: 20 │
│ next: ———————→ │           │                        │           │
│           │    │ next:null │ ←——...more nodes...←———————— :next │ 
└───────────┘    └───────────┘                        └───────────┘ 
                   ↑                                    ↑
                  newEnd                               newHead
在现阶段,我们并不质疑它是如何做到这一点的。我们只是假设它适用于递归情况。因此,如果它工作正常,我们应该得到列表的上述状态

现在,其余语句将链接当前的
head
节点,以便它完成包含此节点的列表的反转作业:

newEnd.next = head
这会产生这种状态:

 head                                                  end
  ↓                                                     ↓
┌───────────┐    ┌───────────┐                        ┌───────────┐
│ value: 85 │    │ value: 15 │                        │ value: 20 │
│ next: ———————→ │           │                        │           │
│           │ ←——————— :next │ ←——...more nodes...←———————— :next │ 
└───────────┘    └───────────┘                        └───────────┘ 
                   ↑                                    ↑
                  newEnd                               newHead
然后我们执行:

head.next = None
这两个赋值使当前
成为从递归返回的反向列表的尾部节点:

 head                                                  end
  ↓                                                     ↓
┌───────────┐    ┌───────────┐                        ┌───────────┐
│ value: 85 │    │ value: 15 │                        │ value: 20 │
│ next:null │ ←——————— :next │ ←——...more nodes...←———————— :next │ 
└───────────┘    └───────────┘                        └───────────┘ 
                   ↑                                    ↑
                  newEnd                               newHead
现在我们只需要告诉调用者哪个是这个反向列表的头和尾节点:

return newHead, head
当你看最后的状态时,你会发现这些确实是颠倒列表的头尾

因此,现在我们知道:

  • 基本情况是有效的(你已经很清楚了)
  • 递归案例的工作条件是,递归正确地为排除第一个节点的列表返回一个反向列表
通过归纳,您可以看到,如果它适用于只有一个节点的列表,那么它也适用于有2个节点、有3个节点等的列表

评论
  • 您链接到的LeetCode问题没有使用
    end
    引用,因此您不需要使用它们
  • 递归有其局限性。当列表很长时,可能会遇到堆栈溢出异常
一种迭代方法是,在沿着列表行走时保留前一个节点的引用,并重新链接每个
next
引用。以下是LeetCode挑战的工作原理(无
end
reference):