【问题标题】:how do I trace through a recursion to reverse a linked list?如何通过递归跟踪以反转链表?
【发布时间】:2021-07-15 10:38:26
【问题描述】:

我有一些反转链表的递归代码:

#我们正在处理ListNodes。

def reverseList(head):
    
    if not head or not head.next:
        return head
    second = head.next
    reverse = reverseList(second)
    second.next = head
    head.next = None

    return reverse

我正在努力遵循纸上的代码,这是我理解的 input 1->2->3->4

head = 1 
second = head.next = 2 
reverse = reverseList(second=2) 
second = 3 
reverse = reverseList(second=3) 
second = 4
reverse = reverseList(second=4) 

since head.next = None, return head (=4) 

second.next(=None) = 4
... and I am so confused. 

我是否正确地跟踪递归?

【问题讨论】:

    标签: python recursion linked-list


    【解决方案1】:

    仅将节点表示为数字对理解算法没有多大帮助。 headsecondreverse 必须是节点引用,并且函数的每个递归调用都有其自己的不同变量集——即使它们的名称与另一个调用中的名称相同。

    为了获得更好的洞察力,假设每个变量都有一个数字后缀,以便根据定义它们的执行上下文来区分它们。因此顶级调用将具有head1second1reverse1、并且第一个递归调用将有自己的head2second2reverse2、...等。

    让我们可视化second 初始化时的列表:

         head1            second1
          ↓                ↓
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: ———————→ │ next: ———————→ │ next: ———————→ │ next:None │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    现在进行递归调用,将引用传递给第二个节点。在递归执行上下文中,这个引用被命名为head,我们也得到了一个新的second引用:

                          head2            second2
                           ↓                ↓
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: ———————→ │ next: ———————→ │ next: ———————→ │ next:None │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    在下一个递归执行上下文中分配了一组类似的变量:

                                           head3            second3
                                            ↓                ↓
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: ———————→ │ next: ———————→ │ next: ———————→ │ next:None │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    最深的递归调用将有它的head 引用最后一个节点:

                                                            head4
                                                             ↓  
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: ———————→ │ next: ———————→ │ next: ———————→ │ next:None │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    这个最深的调用返回head。调用者(有自己的变量集)将该返回值分配给reverse

                                                            reverse3 = head4
                                           head3            second3
                                            ↓                ↓
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: ———————→ │ next: ———————→ │ next: ———————→ │ next:None │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    然后second.next = headhead.next = None 被执行:

                                                            reverse3
                                           head3            second3
                                            ↓                ↓
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: ———————→ │ next: ———————→ │ next: None│ ←——————— :next │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    本次调用结束,返回reverse给调用者。调用者有自己的变量,并将返回的引用分配给他们的reverse 变量:

                          head2            second2          reverse2 = reverse3 
                           ↓                ↓                ↓
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: ———————→ │ next: ———————→ │ next: None│ ←——————— :next │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    然后second.next = headhead.next = None 被执行:

                          head2            second2          reverse2
                           ↓                ↓                ↓
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: ———————→ │ next: None│ ←——————— :next │ ←——————— :next │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    本次调用结束,返回reverse给调用者。调用者有自己的变量,并将返回的引用分配给他们的reverse 变量:

         head1            second1                           reverse1 = reverse2 
          ↓                ↓                                 ↓
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: ———————→ │ next: None│ ←——————— :next │ ←——————— :next │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    然后second.next = headhead.next = None 被执行:

         head1            second1                           reverse1 
          ↓                ↓                                 ↓
        ┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
        │ value: 1  │    │ value: 2  │    │ value: 3  │    │ value: 4  │
        │ next: None│ ←——————— :next │ ←——————— :next │ ←——————— :next │
        └───────────┘    └───────────┘    └───────────┘    └───────────┘ 
    

    本次调用结束,返回reverse给调用者。这确实是完全颠倒名单的新掌门人。

    【讨论】:

    • 非常感谢 Trincot 的解释。两个问题,如果我可能的话 - 首先,当你提到“这个最深的调用返回头。调用者(它有自己的一组变量),将该返回值分配给反向” - 我理解第一部分,但你是什么意思调用者将该返回值分配给反转?因为我在代码中没有看到这一点,也没有对此理解。
    • 其次,为什么reverse会停留在最后一个节点上?
    • 调用者在此处将返回值分配给reversereverse = reverseList(second)。所以reverseList 调用只是返回了它自己的head 引用,reverseList 的调用者获取该引用并将其分配给reverse
    • reverse 停留在最后一个节点的原因:首先,意识到每个reverse 变量都是本地的,并且对于函数的每次执行都是不同的。但是,是的,它必须是这样是合乎逻辑的。反向列表的新头是最后一个节点,每一步都是如此。而且从代码中还可以看到有return reverse,表示调用者从更深层次的递归中获取到引用,然后返回相同的引用,……以此类推。
    • 感谢 Trincot,现在这很有意义。最后一件事 - 对于上面的代码,而不是执行 "reverse = self.reverseList(second)" ;我们可以用“return self.reverseList(second)”替换它,最后当然没有“return reverse”,还有别的东西吗?
    猜你喜欢
    • 2014-07-30
    • 2020-10-02
    • 1970-01-01
    • 1970-01-01
    • 2020-12-04
    • 1970-01-01
    • 1970-01-01
    • 2016-04-13
    • 2021-03-27
    相关资源
    最近更新 更多