今天看到这篇Linus:利用二级指针删除单向链表,作个笔记。
关于在单向链表中删除一个指定的节点,通常有两个易错点。
- 找到指定节点删除时,忘了备份这个节点里指向下一个节点的指针。
- 没有特殊处理删除第一个节点的情况。
01 | //通常的写法 |
02 | typedef struct node |
03 | { |
04 | struct node * next; |
05 | .... |
06 | } node; |
07 | |
08 | typedef bool (* remove_fn)(node const * v); |
09 | |
10 | // Remove all nodes from the supplied list for which the |
11 | // supplied remove function returns true. |
12 | // Returns the new head of the list. |
13 | node * remove_if(node * head, remove_fn rm) |
14 | { |
15 | for (node * prev = NULL, * curr = head; curr != NULL; ) |
16 | { |
17 | node * const next = curr->next; |
18 | if (rm(curr)) |
19 | { |
20 | if (prev) |
21 | prev->next = next; |
22 | else |
23 | head = next; |
24 | free (curr); |
25 | } |
26 | else |
27 | prev = curr; |
28 | curr = next; |
29 | } |
30 | return head; |
31 | } |
01 | //Linus所说的利用二级指针的写法。 |
02 | void remove_if(node ** head, remove_fn rm) |
03 | { |
04 | for (node** curr = head; *curr; ) |
05 | { |
06 | node * entry = *curr; |
07 | if (rm(entry)) |
08 | { |
09 | *curr = entry->next; //分离要删的节点 |
10 | free (entry); //回收内存 |
11 | } |
12 | else |
13 | curr = &entry->next; //->符号优先于& |
14 | } |
15 | } |
第二种写法使用了一个二级指针指向那个指向node节点的指针。关于这个实现,那个文章里已经说的很清楚了。这里只是记录一下对于这个实现的看法。
二级指针的关键在于:二级指针让head指针和next指针在某个特点上处于同一水平。head指针和next最大的不同点是next指针能被别的指针操作。因为next指针属于node结构体中,所以指向node结构体的指针可以携带操作next指针。而head指针没有,没有指针指向它,要修改head的值或者读取head,需要在代码中写明head。而二级指针就解决这个问题,这也就体现了什么是指针。这就是为什么linus说用第一种写法的人不懂指针。因为指针的强大之处在于它能指向任何东西,包括另一个指针。让一个固定了变量名的变量(甚至是一个函数)能作为一个值一样被传递。
是不是必须用二级指针呢?我表示如果单纯为了去掉head指针这个麻烦,我最简单的想法是可以让head指针实体化,即让head先指向一个没用的节点,再让这个节点的next指向链表头节点。
转载请注明:旅途@KryptosX » 利用二级指针删除单向链表——笔记