单链表是一种我们很常见的数据结构,在每一门介绍数据结构的书本中都会详细介绍单链表的结构和性质,单链表由一系列形如下面结构的Node节点组成
typedef struct Node{
struct Node *next;
int value;
}Node;
链表最后一个节点的指针字段的值为NULL。通常我们使用一个根指针来标记链表的起始位置,通过根指针就可以访问整个链表了,根指针只是一个指针,它不含有任何数据。
上面是一个升序排列的链表。下面让我们考虑一下向链表中插入一个新节点。标准的教科书写法——插入一个结点时,需要一个previous的指针,并且这里还需要做一个边界条件的判断——current是否为链表头。于是我们有下面的代码:
1 int insert_if(Node *current,int new_value){
2 Node *previous;
3 Node *new;
4 /*find the right position by going through the list*/
5 while(current != NULL && current->value < new_value){
6 previous = current;
7 current = current->next;
8 }
9 /*allocate memory for the new node*/
10 new = (Node *)malloc(sizeof(Node));
11 if(new == NULL)
12 return FALSE;
13 new->value = new_value;
14
15 /*insert the new node into list*/
16 new->next = current;
17 previous->next = new;
18
19 return TRUE;
20 }
当然这段代码是有问题的,因为当在链表头插入节点时,代码将无法正确工作,为了在链表的起始位置插入一个节点,函数必须修改根指针。但是,函数不能访问变量root,因为root的值的拷贝是作为形参传递给current,任何修改都不会真正反映到root上。解决办法就是把一个指向root的指针作为参数传递给函数,这样就有下面的程序:
1 int insert_if(Node **rootp,int new_value){
2 Node *previous;
3 Node *current
4 Node *new;
5 /*get the point of the first node*/
6 current = *rootp;
7 previous = NULL;
8
9 /*find the right position by going through the list*/
10 while(current != NULL && current->value < new_value){
11 previous = current;
12 current = current->next;
13 }
14 /*allocate memory for the new node*/
15 new = (Node *)malloc(sizeof(Node));
16 if(new == NULL)
17 return FALSE;
18 new->value = new_value;
19
20 /*insert the new node into list*/
21 new->next = current;
22 if(previous==NULL)
23 *rootp = new;
24 else
25 previous->next = new;
26
27 return TRUE;
28 }
第7行赋值后,就可以用于22行判断新值是否应该被添加到链表的起始位置。上面的代码主要是针对在起始位置插入而做一些特殊处理。因为在起始位置插入新节点需要修改的指针是根指针,而对于其他节点,对指针进行的修改实际修改的是前一个节点的next自段。但事实真的是这样么?其实这两个看上去不同的操作实际上是一样的。
消除特殊情况的关键在于:链表中的每个节点都有一个指向它的指针。对于第一个节点,这个指针是根指针;对于其他节点,这个指针是前一个节点的next字段。重点在于每个指针都有一个指针指向它。至于该指针是不是位于一个节点的内部则无关紧要。
我们拥有一个指向当前节点的指针,以及一个“指向当今节点的next字段的”指针,我们不需要previous来指示前一个节点。当移动到下一个节点时,我们保存一个“指向下一个节点的next字段的”指针,而不是保存一个指向当前一个节点的指针。如下面两幅图所示:
图一
图二
这里rootp并不指向节点本身,而是指向节点内部的next字段。代码如下所示:
1 int insert_if(Node **nextp,int new_value){
2 Node *current
3 Node *new;
4
5 /*find the right position by going through the list*/
6 while((current = *nextp) != NULL && current->value < new_value){
7 nextp = ¤t->next;
8 }
9 /*allocate memory for the new node*/
10 new = (Node *)malloc(sizeof(Node));
11 if(new == NULL)
12 return FALSE;
13 new->value = new_value;
14
15 /*insert the new node into list*/
16 new->next = current;
17 *nextp = new;
18 else
19 previous->next = new;
20
21 return TRUE;
22 }
这里分析了利用二级指针在单链表中的应用,可见其可以达到优化代码的作用,当然这一思想也可用于对链表的查找和删除,这里有一篇关于coolshell.cn/articles/8990.html 二级指针在链表中删除的应用,其思想是一样的。