从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。或者你的意思是只是在列表中漫游而不释放和查找某些数据值,如果未找到或未找到,则分别返回零或非零?不,这就是我的意思,感谢澄清!我一定在做一些奇怪的事情,因为我仍然得到了错误的输出,它说它没有在列表中找到某些数字,即使它们确实存在。。。