简体   繁体   English

这个递归 function 如何反转链表?

[英]How does this recursive function to reverse a linked list work?

I found the function below that reverses a linked list recursively:我在下面找到了递归反转链表的 function:

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

I understand the if statements that cover for the base case.我理解涵盖基本情况的if语句。

But I do not understand the recurrence relation.但我不明白递归关系。

How does that recursion work to reverse the list?该递归如何反转列表? Is there a more simple recursive version that reverses a linked list?是否有更简单的递归版本来反转链表? For reference, I am solving LeetCode problem 206. Reverse Linked List :作为参考,我正在解决 LeetCode 问题206。反向链表

Given the head of a singly linked list, reverse the list, and return the reversed list .给定单链表的head ,反转该列表,并返回反转后的列表

I do not understand the recurrence relation.我不明白递归关系。

Let's say we have this linked list:假设我们有这个链表:

 head                                                  end
  ↓                                                     ↓
┌───────────┐    ┌───────────┐                        ┌───────────┐
│ value: 85 │    │ value: 15 │                        │ value: 20 │
│ next: ———————→ │ next: ———————→ ...more nodes...——→ │ next:null │
└───────────┘    └───────────┘                        └───────────┘ 

The recursive part is based on the following observation:递归部分基于以下观察:

If you can reverse a list that is one element shorter, which excludes the current head node, then we should arrive in a situation like this:如果您可以反转一个元素较短的列表,其中不包括当前的head节点,那么我们应该会遇到这样的情况:

 head                                                  end
  ↓                                                     ↓
┌───────────┐    ┌───────────┐                        ┌───────────┐
│ value: 85 │    │ value: 15 │                        │ value: 20 │
│ next: ———————→ │           │                        │           │
│           │    │ next:null │ ←——...more nodes...←———————— :next │ 
└───────────┘    └───────────┘                        └───────────┘ 
                   ↑                                    ↑
                  newEnd                               newHead

At this stage we do not question how it did that.在这个阶段,我们不会质疑它是如何做到的。 We just assume it works for the recursive case.我们只是假设它适用于递归情况。 So if it works correctly, we should get the above state of the list.所以如果它工作正常,我们应该得到上面的 state 列表。

Now the remaining statements will link the current head node so that it finishes the reversal job for a list that includes this node also:现在剩下的语句将链接当前的head节点,以便它完成包含该节点的列表的反转作业:

newEnd.next = head

This produces this state:这产生了这个 state:

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

Then we execute:然后我们执行:

head.next = None

These two assignments have made the current head a tail node to the reversed list we got back from recursion:这两个分配使当前head成为我们从递归返回的反向列表的尾节点:

 head                                                  end
  ↓                                                     ↓
┌───────────┐    ┌───────────┐                        ┌───────────┐
│ value: 85 │    │ value: 15 │                        │ value: 20 │
│ next:null │ ←——————— :next │ ←——...more nodes...←———————— :next │ 
└───────────┘    └───────────┘                        └───────────┘ 
                   ↑                                    ↑
                  newEnd                               newHead

Now we just need to tell the caller which is the head and tail node of this reversed list:现在我们只需要告诉调用者哪个是这个反向列表的头和尾节点:

return newHead, head

When you look at the final state, you see indeed that those are the head and tail of the reversed list.当您查看最终的 state 时,您确实会看到这些是反向列表的头部和尾部。

So, now, we know that:所以,现在,我们知道:

  • The base case works (you were already clear on that)基本案例有效(你已经很清楚了)
  • The recursive case works on the condition that recursion correctly returns a reversed list for the list that excludes the first node递归情况在递归正确返回排除第一个节点的列表的反向列表的条件下起作用

By induction you can then see that if it works for a list with just one node, it also works for a list with 2, with 3, ...etc.通过归纳,您可以看到如果它适用于只有一个节点的列表,它也适用于具有 2、3、...等的列表。

Remarks评论

  • The LeetCode problem you linked to does not use end references, so you should not need to use them.您链接到的 LeetCode 问题不使用end引用,因此您不需要使用它们。
  • Recursion has its limits.递归有其局限性。 When the list is long, you could run into a stack overflow exception.当列表很长时,您可能会遇到堆栈溢出异常。

An iterative method is to keep a reference of the preceding node while you walk along the list and relink each next reference.一种迭代方法是在您沿着列表遍历并重新链接每个next引用时保留前一个节点的引用。 Here is how that works for the LeetCode challenge (no end reference):以下是 LeetCode 挑战的工作原理(无end参考):

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        prev = None
        while head:
            head.next, prev, head = prev, head, head.next
        return prev

Actually, I don't understand that function.实际上,我不明白function。 First, you should only need to be passing to such a function the head of the list;首先,您应该只需要将这样一个 function 传递给列表的头部; I don't know why argument end is being passed or why it even needs to be passed.我不知道为什么要传递参数end或者为什么甚至需要传递它。 Also, argument self suggests that this is a method that is part of a class definition that is not being shown.此外,变量表明这是一种方法,它是未显示的 class 定义的一部分。 Let's start anew:让我们重新开始:

Here is a simple class for defining a node that can be linked to another node and be tagged with a "name" for identification.这是一个简单的 class 用于定义一个节点,该节点可以链接到另一个节点并用“名称”标记以进行识别。 We also include a small function to walk the linked list to print out the nodes:我们还包括一个小的 function 来遍历链表以打印出节点:

class Node:
    def __init__(self, name):
        self.name = name
        self.next = None

a = Node('A')
b = Node('B')
c = Node('C')
d = Node('D')
e = Node('E')
a.next = b
b.next = c
c.next = d
d.next = e

def print_list(head):
    while head:
        print(head.name)
        head = head.next

print_list(a)

Prints:印刷:

A
B
C
D
E

Next we present the recursive function to reverse such a list.接下来我们提出递归 function 来反转这样的列表。 This function is passed the head node of the list, asserted to be not None .这个 function 被传递给列表的头节点,断言不是None The rest I have tried to explain as comments at each step of the algorithm. rest 我试图在算法的每个步骤中将其解释为注释。 A key point is comment 7: make what we had been pointing to now point back to us .一个关键点是评论 7:让我们现在指向的东西指向我们 This relates to comment 5: we now point backwards to None in that when we set head.next = None it will only stay None if this node was the head node of the entire list.这与注释 5 相关:我们现在向后指向 None ,因为当我们设置head.next = None时,它只会在该节点是整个列表的头节点时保持None Otherwise, per comment 7, it will later on be updated to point back to a previous node.否则,根据评论 7,它将稍后更新以指向前一个节点。

def reverse(head):
    assert(head) # 1. make sure head is not None
    next_head = head.next # 2. get the next node, if any
    if next_head is None: # 3. if we do not point to a next node, then
        return head # 4. just return this node as the new head
    head.next = None # 5. we now point backwards to None
    # 6. otherwise, reverse recursively what we were pointing to and get the new head
    new_head = reverse(next_head)
    # 7. make what we had been pointing to now point back to us:
    next_head.next = head
    # 8. and return the new head
    return new_head

# without comments for clarity:
def reverse_no_comments(head):
    assert(head)
    next_head = head.next
    if next_head is None:
        return head
    head.next = None
    new_head = reverse(next_head)
    next_head.next = head
    return new_head

def print_list(head):
    while head:
        print(head.name)
        head = head.next
        

print('Reversed:')
new_head = reverse(a)
print_list(new_head)

Prints:印刷:

Reversed:
E
D
C
B
A

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM