在TED上做过一次访谈,linus提到了关于编程的品味,他举了一个例子:
linus在TED演讲_哔哩哔哩_bilibili
见原视频15'前后
这个例子要删掉一个链表的节点项
传入的参数是一个节点的引用
解法1,使用了prev
remove_list_entry(entry) { prev = NULL; walk = head; while (walk && walk!=entry) { prev = walk; walk = walk->next; } // Remove the entry by // updating the head or previous entry if (!prev) { head = entry->next; } else { prev->next = entry->next; } // 释放节点空间 free(entry); }
解法2,使用了二重指针
remove_list_entry(entry) { // The "inderect" pointer points to the // *address* of of the thing we will update indirect = &head; // Walk the list, looking for the thing that points to // entry we want to remove entry while ((*indirect)&&(*indirect)!=entry) { indirect = &(indirect->next); } // just remove the entry *indirect = entry->next; free(entry); }
用一个前驱节点 prev 来寄存待删除节点之前的节点
用图形表示就是
这样,就能实现将prev指向entry的后继节点
为了保证可靠性,也需要考虑到
此时需要我们判断 prev=NULL
并让head->next直接指向entry->next
此时可能需要直接释放 head 所指向的节点空间
并让head指向NULL
指针是一次寻址
二重指针是二重寻址
indirect=&head
head本身是指向一个节点的指针,而indirect存储了head的地址,所以是存储指针的容器
这里不得不提到节点的定义
typedef struct ListNode { ListNode* next; int data; } ListNode;
每个节点其实都存储了
大概类似这样
注意我在上面加了一个地址0x0000000
其实next的内容,就是地址,指针就是保存了地址的变量。
那么把整个链表放在一起看,是这样的:
每个节点的next字段,都保存了它下一个节点的地址
那么,我们可以认为,每个节点的next字段就是一个指针变量
而 indirect = &head
就是一个二重指针
indirect存放了head的地址
可以通过解引用运算获取head里面的值
那么,究竟如何完成删除一个节点的功能?
为了方便叙述,给每个next字段都加上一个地址 ,分别记作ABCD
假设我们要删掉0x00000008这个节点
此时indirect已经指向head了
while (*indirect && *indirect != entry) { indirect = &(*indirect->next); }
这段循环,让indirect指向了 存放entry地址 的next字段
即此时 indirect = B
此时,如果让B里面的内容,从0x00000008
变为0x00000010
就能完成删除节点,维护链表的操作
代码如下
// just remove the entry *indirect = entry->next; free(entry);
示意图
像其他的情况,如
也可以很好的解决,如有兴趣可以自己实现一下。
对编程的爱好,让我充满力量,加油!