从C中指针指向的列表中删除数据

从C中指针指向的列表中删除数据,c,recursion,linked-list,nodes,C,Recursion,Linked List,Nodes,我已经尝试了一段时间,想找出一种递归的方法来删除一个节点(从链表中的任何位置),而不留下任何内存泄漏 这可能吗 以下是我的非递归方法,它不起作用: int removeItem(struct ListNode** headRef, int data) { struct ListNode* temp = *headRef; // store original head int val = data; while (temp->next != NULL)

我已经尝试了一段时间,想找出一种递归的方法来删除一个节点(从链表中的任何位置),而不留下任何内存泄漏

这可能吗

以下是我的非递归方法,它不起作用:

    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;
 }

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

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;
}
如果是,不要。最好通过迭代来完成这项任务(您可能会发现理解起来有点困难,但如果您理解递归解决方案,我无法想象为什么):

它们是如何工作的

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

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


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

递归方法需要额外的参数。最初,它的名称如下:

...
rc=removeItem(&headRef, NULL, NULL, 42);
...
int removeItem(
      struct ListNode **headRef,
      struct ListNode  *parentNode,
      struct ListNode  *currentNode,
      int data
      )
   {
函数的递归版本可以如下实现:

...
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;
否则,修改父节点以跳过当前节点,并指向下一个节点

         parentNode->next = currentNode->next;
      parentNode=currentNode;
现在可以安全地释放currentNode

      free(currentNode);
         free(currentNode);
         return 1;
         }
完成了

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

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

也许它可以这样实现(iteritive,而不是recursive):

遍历所有列表节点

   while(currentNode)
      {
就是这个吗

      if(currentNode->data == data)
         {
是的,这是一场比赛

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

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

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

好的,它不是头节点,它是一个更晚的兄弟节点。若要取消此节点的链接,必须将此节点的父节点修改为指向当前节点之外

         *headRef = currentNode->next;
      else
            parentNode->next = currentNode->next;
现在currentNode已从列表中取消链接,可以安全地将其释放

      free(currentNode);
         free(currentNode);
         return 1;
         }
请记住此节点是下一个节点的父节点

         parentNode->next = currentNode->next;
      parentNode=currentNode;
移动到列表中的下一个节点

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

    return 0;
    }

让我们看看您的代码,您可能已经接近了。将上一个条目指向下一个条目,然后释放与删除的条目关联的内存。我不确定您所说的“递归方式”是什么意思由于从列表中删除节点只是一个简单的步骤,我想使用递归来简化函数,但我不能将我的大脑围绕它…嗯…好的,是的,我不确定我是否理解那里发生了什么。下面是对函数应该做什么的描述:从headRef指向的列表中删除数据,必要时更改head。*不要假设列表是否已排序。*应释放已删除节点的内存。*如果数据存在,则返回1;如果找不到,则返回0。@user3366369是的,以上两种方法都会这样做。您应该注意,赋值不包括删除指定值的所有元素。如果是这样的话,这两种情况会有所不同(但不会有太大差别)。我会提示你它们是如何工作的;他们使用
headRef
作为局部变量,使用这些节点中的指针(不仅仅是指针包含的地址)从一个节点移动到下一个节点。与
headRef
var最初保存头指针地址的方式相同,我们也使用它来保存链接节点中
next
指针的地址。我如何添加一个检查,以查看放入的数据是否确实在列表中?这也可以做到这一点。如果数据在列表中,则返回1;如果不在列表中,则返回0。或者你的意思是只是在列表中漫游而不释放和查找某些数据值,如果未找到或未找到,则分别返回零或非零?不,这就是我的意思,感谢澄清!我一定在做一些奇怪的事情,因为我仍然得到了错误的输出,它说它没有在列表中找到某些数字,即使它们确实存在。。。