今天看到这篇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 » 利用二级指针删除单向链表——笔记