微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在链表中使用节点指针如何改变递归?

如何解决在链表中使用节点指针如何改变递归?

我使用了临时节点'nextnode'指向headcur的下一个节点:

SinglyLinkedListNode* removeDuplicates(SinglyLinkedListNode* head) {
    if ( head == NULL ) return NULL;
    SinglyLinkedListNode *nextnode = head->next;
    while ( nextnode != NULL && head->data == nextnode->data ) {
        nextnode = nextnode->next;
    }
    head->next = removeDuplicates( nextnode );
    return head;
} 

代码可以正常工作...但是如果我更改

head->next = removeDuplicates( nextnode );

head->next = removeDuplicates( head->next);

代码输出的列表与输入的列表相同。

但是,如果我在所有位置删除nextnode并使用head->next代替nextnode,则代码可以正常工作!

实际上发生了什么,这使它起作用了?我想念什么?

使用head->nextnextnode时此代码会发生什么?我只想了解它在内存分配中的工作原理,以便更好地了解递归。

解决方法

以该列表为例:

head
  ↓
  1 → 2 → 2 → 3 → 3 → 3 → 4

在函数的第一次调用中,nextnode将指向[2],并且由于数据不同(1!= 2),while条件将立即为false。所以我们有:

head  nextnode
  ↓   ↓  
  1 → 2 → 2 → 3 → 3 → 3 → 4

然后进行第一个递归调用,在该执行上下文中,还有另一个head变量,该变量设置为调用者的nextnode的值。为了更好地跟踪此head变量,我将其称为head2,并在原始调用中谈到变量时将继续引用head

head head2
  ↓   ↓  
  1 → 2 → 2 → 3 → 3 → 3 → 4

创建了一个新的nextnode变量,这一次while循环将进行一次迭代,因为存在重复值(2)。当nextnode指向3:

的节点时,迭代停止。
head head2  nextnode
  ↓   ↓       ↓   
  1 → 2 → 2 → 3 → 3 → 3 → 4

进行另一个递归调用。再次,我们得到一个新的head变量,我将其称为head3

head head2  head3
  ↓   ↓       ↓   
  1 → 2 → 2 → 3 → 3 → 3 → 4

同样,将创建一个新的nextnode。这次它最终指向带有4的节点:

head head2  head3       nextnode
  ↓   ↓       ↓           ↓
  1 → 2 → 2 → 3 → 3 → 3 → 4

进行了更深层次的递归调用:

head head2  head3       head4
  ↓   ↓       ↓           ↓
  1 → 2 → 2 → 3 → 3 → 3 → 4

这次,新的nextnode变量将变为NULL,因此我们使用参数NULL进行了递归调用。我们到了递归的结尾,因为最深的调用将立即返回NULL。我们返回到进行呼叫的地方,看到已为head4->next分配了NULL。在这种情况下,此分配没有任何区别,因为head4->next已经是NULL

我们回溯到递归树中的上一级:返回head4。在那里,我们确实看到了修改。此返回的head4被分配给head3->next,因此,仅通过一次分配就将值3的第二个和第三个节点踢出了列表:

head head2  head3       head4
  ↓   ↓       ↓           ↓
  1 → 2 → 2 → 3 →         4

我们又回退了一步,这次head3返回并分配给head2->next。现在将值2的第二个节点踢出:

head head2  head3   head4
  ↓   ↓       ↓       ↓
  1 → 2 →     3 →     4

又回溯了一次:head2返回到分配给head->next。由于head->next已指向head2,因此没有区别。顶层函数调用返回head,我们完成了。

使用head->next = removeDuplicates( head->next);

此更改确实会破坏算法。它使nextnode的值无关紧要。 while循环现在什么也不做,因为代码没有使用nextnode的新值。您也可以删除该循环和变量nextnode,然后很明显,从未删除任何节点。

一个关键的观察结果是removeDuplicates总是返回您传递给它的节点作为参数,因此更改后的语句确实在做:

head->next = head->next

...什么都不做,这是列表链被更改的代码中的 only 位置。 next属性在其他任何地方都没有更改。

如果我在所有位置都删除了nextnode,并用head->next代替了nextnode,则代码运行正常!

的确。在那种情况下,该函数不需要返回任何内容,因为重复删除永远不需要更改列表的head。因此,代码如下所示:

void removeDuplicates(SinglyLinkedListNode* head) {
  if ( head == NULL ) return;
  while ( head->next != NULL && head->data == head->next->data ) {
    head->next = head->next->next;
  }
  removeDuplicates( head->next );
}

对于示例列表,我们可以再次进行与该答案顶部相同的分析(请在纸上进行),您将得出结论,这也将有效地删除重复项。区别在于,此处的实际删除操作是在while循环中一次出现一个节点(每个迭代恰好删除了一个节点),而在原始代码中,它是在回溯期间发生的,它实际上可以删除多个节点。一口气(就像我们在一次分配中将3 → 3 → 3 → 4变成3 → 4的情况一样),因此通常来说,原始代码有时可以用更少的分配量完成工作,当然当重复很多列表中的值。

原始代码中的->next引用也较少。

由于这些差异,理论上原始代码将运行得更快。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。