【问题标题】:Removing data from list pointed to by a pointer in C从C中指针指向的列表中删除数据
【发布时间】:2014-04-24 04:48:06
【问题描述】:

我一直在尝试找出一种递归方式来删除节点(从链表中的任何位置),而不会留下任何内存泄漏。

这可能吗??

这是我不起作用的非递归方式:

    int removeItem(struct ListNode** headRef, int data)
    {
    struct ListNode* temp = *headRef; // store original head
    int val = data;
    while (temp->next != NULL)
    {
    if (temp->next->data != val)
    {
    temp = temp->next;
    }
    else if (temp->next->data == val)
    {
    temp = temp->next;
    free(temp);
    return 1;
  }
  else
  {
  return 0;
   }
 }
 return 0;
 }

【问题讨论】:

  • 让我们看看你的代码,你可能已经接近了。
  • 使前一个入口指向下一个入口,然后释放与已删除入口相关的内存。
  • 我不确定您所说的“递归方式”是什么意思,因为从列表中删除节点只是一个步骤
  • 我想使用递归来简化函数,但我无法将我的大脑围绕它...

标签: c recursion linked-list nodes


【解决方案1】:

老实说,我不确定我是否遵循了这个问题,但是是你想要做的吗?

int removeItem(struct ListNode** headRef, int data)
{
    if (!*headRef)
        return 0;

    if ((*headRef)->data != data)
        return removeItem(&(*headRef)->next, data);

    // found it. drop it out.
    struct ListNode *tmp = *headRef;
    *headRef = tmp->next;
    free(tmp);
    return 1;
}

如果是,不要。最好通过迭代来完成这项任务(您可能会发现它有点难以理解,但如果您理解递归解决方案,我无法想象为什么):

int removeItem(struct ListNode** headRef, int data)
{
    while (*headRef && (*headRef)->data != data)
        headRef = &(*headRef)->next;

    if (*headRef)
    {
        struct ListNode *tmp = *headRef;
        *headRef = tmp->next;
        free(tmp);
        return 1;
    }
    return 0;
}

它们的工作原理

这两个函数都通过使用headRef 来始终保存引用我们正在测试的节点的指针 地址。

最初headRef 包含列表的head 指针的地址。每次我们需要移动到下一个节点时,我们都会在headRef 中加载指向该节点的指针地址。当我们最终找到我们要寻找的节点时,引用它的指针的地址在headRef 中(可能是原始的head 指针,或者是列表中某处的某个next 指针;哪一个真的不重要)。

这两个函数之间的区别是直截了当的。首先,我们使用递归将要测试的下一个指针的地址传递给递归调用。在第二个中,我们只使用一个迭代循环。无论哪种方式,headRef 始终保存指向我们正在测试的节点的指针的地址。如果我们找到赢家(在本例中为输家),请注意两个函数执行相同的操作:将值保存在*headRef 中(我们的指针正在释放) ,用当前节点的next 的值填充*headRef,然后删除旧节点。把它画在纸上会有很多帮助(我的 ascii 艺术 sucks; sry)。

【讨论】:

  • 嗯...好吧,是的,我不确定我是否理解那里发生的事情。以下是该函数应该做什么的描述:从 headRef 指向的列表中删除数据,必要时更改头部。 * 不假设列表是否已排序。 * 应释放已删除节点的内存。 * 如果数据存在则返回 1,如果没有找到则返回 0。
  • @user3366369 是的,以上两个都是这样做的。您应该注意,该赋值 包括删除指定值的所有 元素。如果情况不同,这两种情况都会有所不同(但差别不大)。我会提示你它们是如何工作的;他们使用headRef 作为局部变量,使用指针in 这些节点(不仅仅是这些指针包含的地址)从一个节点走到下一个节点。与headRef var 最初保存头指针地址的方式相同,我们也使用它来保存链接节点中next 指针的地址。
  • 如何添加检查以查看放入的数据是否确实在列表中?
  • 这也是如此。如果数据 列表中,则返回 1,否则返回 0。或者您的意思只是在列表中遍历而不释放和查找某些数据值,如果分别返回零或非零,则返回零或非零?
  • 不是我的意思,感谢您的澄清!我一定是在做一些其他奇怪的事情,因为我仍然得到错误的输出,它说它没有在列表中找到某些数字,即使它们实际上在那里......
【解决方案2】:

递归方法需要额外的参数。最初,它会这样调用:

...
rc=removeItem(&headRef, NULL, NULL, 42);
...

函数的递归版本可以这样实现:

int removeItem(
      struct ListNode **headRef,
      struct ListNode  *parentNode,
      struct ListNode  *currentNode,
      int data
      )
   {

如果这是第一次调用此函数,请将 currentNode 设置为列表的头部。

   if(NULL == currentNode)
      currentNode=*headRef;

这是要删除的节点吗?

   if(currentNode->data == data)
      {

这是头节点吗?

      if(currentNode == *headRef)

如果是head,修改headRef指向当前节点以外的节点。

         *headRef = currentNode->next;
      else

否则修改父节点跳过当前节点,指向下一个节点。

         parentNode->next = currentNode->next;

currentNode 现在可以安全地释放了。

      free(currentNode);

完成。

      return(1);
      }

如果 currentNode 不是我们要查找的节点,则使用下一个节点递归回该函数。 (除非我们已经到达列表的末尾)。

return(
   currentNode->next 
      ? removeItem(headRef, currentNode, currentNode->next, data)
      : 0
   );
}

================================================ ===================

也许可以这样实现(迭代,而不是递归):

int removeItem(
      struct ListNode **headRef, 
      int data
      )
   {
   struct ListNode *currentNode = *headRef; // Copy the address of headref. 
   struct ListNode *parentNode = NULL; 

遍历所有列表节点。

   while(currentNode)
      {

是这个吗?

      if(currentNode->data == data)
         {

是的,这是一场比赛。

删除此节点的第一步是将其从列表中取消链接。取消链接的方法取决于这是头节点还是后面的兄弟节点。

这是头(或第一个)节点吗?

         if(NULL == parentNode)

如果是头节点,则必须调整headRef指向下一个(或第二个)节点。

            *headRef = currentNode->next;
         else

好的,它不是头节点,它是后来的兄弟节点。要取消链接此节点,必须修改此节点的父节点以指向当前节点之外。

            parentNode->next = currentNode->next;

现在 currentNode 已从列表中取消链接,可以安全地释放它。

         free(currentNode);
         return 1;
         }

记住这个节点是下一个节点的父节点。

      parentNode=currentNode;

移动到列表中的下一个节点。

      currentNode = currentNode->next;
      }

    return 0;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-10
    • 2010-09-08
    • 2013-07-02
    • 2020-06-24
    • 2017-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多