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

时间:2014-04-24 04:48:07

标签: 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)
    {
    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;
 }

2 个答案:

答案 0 :(得分: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;
}

如果是,。最好通过迭代来完成这个任务(你可能会发现 little 更难理解,但我无法想象为什么,如果你理解递归解决方案):

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中(我们的指针是释放),使用当前节点*headRef的值填充next,然后删除旧节点。在纸上绘图将有助于吨(我的ascii艺术糟透了; sry)。

答案 1 :(得分:0)

递归方法需要额外的参数。最初,它将被称为:

...
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)

如果是头部,请修改headRef以指向当前节点之外的节点。

         *headRef = currentNode->next;
      else

否则,修改父节点以跳过currentNode,并指向下一个节点。

         parentNode->next = currentNode->next;

现在可以安全地释放currentNode。

      free(currentNode);

完成。

      return(1);
      }

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

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

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

也许它可以用这种方式实现(iteritive,而不是递归):

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