我一直试图找出一种递归方式来删除节点(从链表中的任何位置),而不会留下任何内存泄漏。
这可能吗?
这是我的非递归方式不起作用:
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;
}
答案 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;
}