从单链表中删除一个条目
Posted
技术标签:
【中文标题】从单链表中删除一个条目【英文标题】:delete an entry from a singly-linked list 【发布时间】:2019-01-18 12:53:00 【问题描述】:所以今天我在看The mind behind Linux | Linus Torvalds,Linus在视频中贴了两段代码,都是用来删除单链表中的某个元素的。
第一个(这是正常的):
void remove_list_entry(linked_list* entry)
linked_list* prev = NULL;
linked_list* walk = head;
while (walk != entry)
prev = walk;
walk = walk->next;
if (!prev)
head = entry->next;
else
prev->next = entry->next;
还有更好的:
void remove_list_entry(linked_list* entry)
// The "indirect" pointer points to the
// *address* of the thing we'll update
linked_list** indirect = &head;
// Walk the list, looking for the thing that
// points to the entry we want to remove
while ((*indirect) != entry)
indirect = &(*indirect)->next;
// .. and just remove it
*indirect = entry->next;
所以我无法理解第二段代码,当*indirect = entry->next;
求值时会发生什么?我不明白为什么它会导致删除某个条目。请哪位大神解释一下,谢谢!
【问题讨论】:
【参考方案1】:
*indirect = entry->next;
求值时会发生什么?我不明白为什么它会导致删除某些条目。
希望你对双指针有清晰的认识1).
假设如下: 节点结构是
typedef struct Node
int data;
struct Node *next;
linked_list;
并且链表有5
节点和指向列表中第二个节点的entry
指针。内存中的视图是这样的:
entry -+
head |
+---+ +-------+ +-------+ +-------+ +-------+ +--------+
| |---->| 1 | |---->| 2 | |---->| 3 | |---->| 4 | |---->| 5 |NULL|
+---+ +-------+ +-------+ +-------+ +-------+ +--------+
此声明:
linked_list** indirect = &head;
将使indirect
指针指向head
。
entry -+
head |
+---+ +-------+ +-------+ +-------+ +-------+ +--------+
| |---->| 1 | |---->| 2 | |---->| 3 | |---->| 4 | |---->| 5 |NULL|
+---+ +-------+ +-------+ +-------+ +-------+ +--------+
^
|
+---+
| |
+---+
indirect
while
循环
while ((*indirect) != entry)
*indirect
将给出第一个节点的地址,因为head
指向第一个节点,并且由于entry
指向第二个节点,循环条件计算为true
,然后将执行以下代码:
indirect = &(*indirect)->next;
这将使indirect
指针指向第一个节点的next
指针。内存视图:
entry -+
head |
+---+ +-------+ +-------+ +-------+ +-------+ +--------+
| |---->| 1 | |---->| 2 | |---->| 3 | |---->| 4 | |---->| 5 |NULL|
+---+ +-------+ +-------+ +-------+ +-------+ +--------+
^
|
+---+
| |
+---+
indirect
现在将评估while
循环条件。因为indirect
指针现在指向第一个节点的next
,所以*indirect
将给出第二个节点的地址,并且由于entry
指向第二个节点,循环条件计算为false
并且循环退出.
现在将执行以下代码:
*indirect = entry->next;
*indirect
取消对第一个节点的next
的引用,现在它被分配了entry
指针指向的节点的next
。内存视图:
entry -+
head |
+---+ +-------+ +-------+ +-------+ +-------+ +--------+
| |---->| 1 | |-- | 2 | |---->| 3 | |---->| 4 | |---->| 5 |NULL|
+---+ +-------+ \ +-------+ +-------+ +-------+ +--------+
*indirect \ /
+------------+
现在第一个节点的next
指向列表中的第三个节点,这样第二个节点就从列表中删除了。
希望这能消除您的所有疑虑。
编辑:
David 在评论中建议添加一些细节 - 为什么&(*indirect)->next
中需要 (..)
括号?
indirect
的类型是linked_list **
,也就是说它可以保存linked_list *
类型的指针的地址。
*indirect
将给出linked_list *
类型的指针,->next
将给出其next
指针。
但我们不能写*indirect->next
,因为运算符->
的优先级高于一元运算符*
。因此,*indirect->next
将被解释为*(indirect->next)
,这在语法上是错误的,因为indirect
是指向指针的指针。
因此我们需要()
围绕*indirect
。
另外,&(*indirect)->next
将被解释为&((*indirect)->next)
,即next
指针的地址。
1)如果您不知道双指针是如何工作的,请查看以下内容:
举个例子:
#include <stdio.h>
int main()
int a=1, b=2;
int *p = &a;
int **pp = &p;
printf ("1. p : %p\n", (void*)p);
printf ("1. pp : %p\n", (void*)pp);
printf ("1. *p : %d\n", *p);
printf ("1. *pp : %d\n", **pp);
*pp = &b; // this will change the address to which pointer p pointing to
printf ("2. p : %p\n", (void*)p);
printf ("2. pp : %p\n", (void*)pp);
printf ("2. *p : %d\n", *p);
printf ("2. *pp : %d\n", **pp);
return 0;
在上面的代码中,在这条语句-*pp = &b;
中,你可以看到不直接访问指针p
我们可以使用双指针pp
来改变它指向的地址,它指向的是指针@ 987654377@,因为取消引用双指针pp
将给出指针p
。
它的输出:
1. p : 0x7ffeedf75a38
1. pp : 0x7ffeedf75a28
1. *p : 1
1. *pp : 1
2. p : 0x7ffeedf75a34 <=========== changed
2. pp : 0x7ffeedf75a28
2. *p : 2
2. *pp : 2
内存中的视图是这样的:
//Below in the picture
//100 represents 0x7ffeedf75a38 address
//200 represents 0x7ffeedf75a34 address
//300 represents 0x7ffeedf75a28 address
int *p = &a
p a
+---+ +---+
|100|---->| 1 |
+---+ +---+
int **pp = &p;
pp p a
+---+ +---+ +---+
|300|---->|100|---->| 1 |
+---+ +---+ +---+
*pp = &b;
pp p b
+---+ +---+ +---+
|300|---->|200|---->| 2 |
+---+ +---+ +---+
^^^^^ ^^^^^
【讨论】:
哦,好答案。作为一名新的 C 程序员,我唯一挥之不去的问题是,为什么(..)
需要在 &(*indirect)->next
周围加上括号?
@H.S.现在作为一个新的 C 程序员,我会完全理解它。该死的好努力。【参考方案2】:
该条目并没有真正“删除”,它只是不再在列表中。 如果这是您的连锁店:
A --> B --> C --> D --> E --> ■
而你想删除 C,你实际上只是在链接它。它仍在内存中,但不再可以从您的数据结构中访问。
C
A --> B --------> D --> E --> ■
最后一行将 B 的 next
指针设置为 D 而不是 C。
【讨论】:
我无法理解它是如何通过*indirect = entry->next;
链接元素的。【参考方案3】:
不像第一个示例那样循环遍历列表中的条目,第二个示例循环遍历指向列表中条目的指针。这允许第二个示例对您所询问的语句得出简单的结论,英文是“设置用于指向我要从列表中删除的条目的指针,以便它现在指向该条目的任何内容指向”。换句话说,它使指向您要删除的条目的指针超过您要删除的条目。
第一个示例必须有一种特殊的方式来处理要删除的条目的唯一情况,即列表中的第一个条目。因为第二个示例循环遍历指针(以 &head 开头),所以它没有特殊情况。
【讨论】:
【参考方案4】:*间接=入口->下一个; 那只是将它移动到下一个节点 您需要删除条目一 所以你必须在入口节点之前指向入口节点的下一个 所以你的循环应该在进入之前停止 而((*间接)->下一个!=条目) 间接 = &(*间接)->下一个
(*indirect)->下一个=entry->下一个
希望对你有帮助
【讨论】:
你需要保存条目..在下一个点之后释放它 那是令人困惑的部分,入口还在,它并没有丢失在那段代码中。 是的 .. 如果您使用 malloc func .. 或 realloc 对该链表进行编码,那么您需要释放要删除的节点 .. 只需使用 free 函数 free(entry) 但在在你指向下一个之后,最后一个是平均值 并且不要忘记将条目之前的节点的下一个..更改为下一个它最重要的部分 谢谢,但我的意思是这段代码无需任何修改就可以正常工作,如果您愿意,可以尝试..以上是关于从单链表中删除一个条目的主要内容,如果未能解决你的问题,请参考以下文章
线性表练习之Example002-删除递增非空单链表中的重复值域节点