我正在阅读"C Programming: A Modern Approach" by KN King这本书,在第17章。在第432页,它们定义了一个从单链表中删除节点的函数:
下面的函数delete_from_list
使用了我们刚刚概述的策略。当给定一个列表和一个整数n
时,该函数删除包含n
的第一个节点。如果没有节点包含n
,则delete_from_list
不执行任何操作。在这两种情况下,函数都会返回一个指向列表的指针。
struct node *delete_from_list(struct node *list, int n)
{
struct node *cur, *prev;
for (cur = list, prev = NULL;
cur != NULL && cur->value != n;
prev = cur, cur = cur->next)
;
if (cur == NULL)
return list; /* n was not found */
if (prev == NULL)
list = list->next; /* n is in the first node */
else
prev->next = cur->next; /* n is in some other node */
free(cur);
return list;
}
这本书在第454页的练习中提到了这个函数:
练习
1.修改delete_from_list
函数,使其只使用一个指针变量,而不是两个(cur
和prev
)。
我的问题是关于我在Github上找到的以下解决方案:
struct node *delete_from_list(struct node **list, int n) {
struct node *item = *list;
while (item) {
if (item->value == n) {
*list = item->next;
free(item);
break;
}
list = &item->next;
item = item->next;
}
return *list;
}
我知道指针指向指针是什么。但我不明白struct node **list
行之后会发生什么。如果有人能用可视化的方式逐行解释这个解决方案,我将非常感激。
1条答案
按热度按时间dfty9e191#
如果有人能用可视化的方式逐行解释这个解决方案,我将非常感激。
让我们举一个链表的例子,它的值为1、2和3,其中
head
指向它的第一个节点,我们有一个值n
等于2。我们可以将主程序中列表的状态描述如下:使用这个解决方案代码,调用者必须传递一个指向节点的指针,因此如果主程序中的
head
变量是指向第一个节点的指针,我们需要传递它的 address,如下所示:现在让我们想象一下函数如何看到自己的
list
变量:第一个语句是:
所以我们得到:
执行进入循环,但
if
条件为false,因此下一个执行的语句是:现在,本地变量
list
将指向item
所指向的节点的next
成员的地址:循环体中的最后一条语句是:
所以我们得到:
在下一次迭代中,
if
条件为真,因此我们执行以下操作:这会使列表发生如下变化:
现在可以使用
free(item)
释放找到的节点:最后,函数返回
*list
,但这没有什么用途:调用者不应该使用该返回值来赋值给自己的head
变量,否则会出错。调用者应该只依赖函数来适应它的head
,如果有必要的话(不是这个例子中的情况)。这是呼叫者最终得到的结果:
备注
在我看来,这并不是一个“公平”的解决方案,因为它改变了函数的签名:
原始函数必须这样调用:
...而这个函数应该这样调用:
基于上述原因,我不确定这是否是这项工作的预期解决方案。有关解决此问题的其他想法,请参见this Q&A。