我是学生(还是菜鸟),我有一个问题。我必须删除单向链表中的一对节点,其总和等于k。 例如,如果链表是13->4->5->4->3和k=9,那么我的函数应该将列表转换为13->3(4+5=9, 5+4=9)。这是我未完成的代码:
typedef struct _node
{
int value;
struct _node* next;
}Node;
void remove(Node **list, int k)
{
if(*list == NULL || (*list)->next == NULL)
return;
Node *prev = *list;
Node *tmp = (*list)->next;
while(tmp != NULL)
{
if(prev->value + tmp->value == k)
{
free(prev);
prev = tmp->next;
free(tmp);
tmp = tmp->next->next;
}
}
}
我有一个想法,但我不知道如何在 while 循环中设置条件。 有人知道如何重写吗?
现在你在第一场比赛中断开了链接:在你的例子中,在你删除 4 后,13 的next
仍然指向它,悬空的指针也是如此。但这不会发生,因为你永远不会从 13 移动到任何地方,因为只有当你删除一个元素时,你才会向前迭代(这对 13->4 对不会发生)。
您应该考虑的第三件事是,您实际上应该标记要删除的元素,否则您将无法删除示例中的后 4 个。
First --> 您没有在列表中前进,因此将导致无限循环。
第二 --> 如果tmp
的下一个节点是NULL
那么tmp=tmp->next->next
会给你分段错误。
第三 --> 当你释放内存时,你也会破坏链表,因此pointToPrev
指针已在底层代码中声明,它将指向prev
指针后面的节点,但在释放内存的情况下,它将指向网络节点以tmp
保存列表。
最好将 while 循环更改为:
` int indicator=0;
Node *pointToPrev = *list;
if(prev->value + tmp->value == k)
{
*list=temp->next;
free(tmp);
free(prev);
indicator=2;
}
while((tmp != NULL)&&(indicator!=2))
{
if(prev->value + tmp->value == k)
{
free(prev);
pointToPrev->next = tmp->next;
free(tmp)
break; //AS YOU DON'T NEED TO COMPLETE THE ENTIRE WHILE LOOP AS SUM HAS BEEN ALREADY FOUND
}
else //THIS WILL SAVE YOU FROM THE INFINITE LOOP
{
tmp = tmp->next;
prev = prev->next;
if(indicator==0)
{
pointToPrev = *list;
indicator=1;
}
else
pointToPrev=pointToPrev->next;
}
}
这个问题很复杂,因为您有两个重叠的条件来控制是否应删除给定节点:如果其值与前一个节点的值之和是目标值,或者如果其值与下一个节点的值之和是目标值。 更重要的是,除了它是最后一个节点外,您将始终需要将下一个节点与它后面的节点进行比较,因此您必须记住它所承载的值,否则您必须避免删除它。
这个问题可以通过从第二个节点开始的普通链表遍历来解决(或者如果节点少于两个,则无需执行任何操作)。 在每次迭代时
- 确定当前节点和先前节点的节点值之和是否等于目标值
- 如果是这样,或者该评估在上一个循环迭代中为 true,则删除上一个节点
循环终止后,根据需要删除最后一个节点。 您需要在迭代之间携带足够的状态,以便适当地执行删除并记住上一次迭代中求和计算的结果,但总的来说,这并不比按值删除单个节点困难多少。
这种方法非常简单,因为它将每次评估中必须考虑的事例数限制为两个,它从不需要在列表中比下一个节点更靠前看,并且当它删除节点时,该节点始终与当前节点具有相同的关系。 此代码保留为应有的练习。