最近两天看《C和指针》这本书的链表部分(第十二章),感觉作者写得实在是太棒了,C指针用得太漂亮了。自己看完后稍微有点心得,记录在此以便日后复习。
这里只讨论双链表(Doubly Linked List),更具体的说是有序双链表的插入问题。
所谓双链表,就是链表中每个节点含两个节点类型的指针,一个指向下一个节点,另外一个指向上一个节点。
链表节点数据结构定义如下:
typedef struct NODE{
struct NODE *fwd;// forward---pointer to the next node in the list
struct NODE *bwd;//backward---pointer to the previous node in the list
int value;
} Node;
![](https://img-my.csdn.net/uploads/201210/30/1351532912_9693.jpg)
如上图所示,根节点的fwd指向链表中第一个节点,第一个节点的fwd指向第二个节点,以次类推,注意最后一个节点的fwd指向NULL;最后一个节点的bwd指向前一个节点,以此类推,注意第一个节点的bwd指向NULL;此外,要注意根节点的bwd也是指向最后一个节点的。
这样,我们可以从两个方向来遍历双链表的节点,使得链表操作更加方便快捷。
对于有序双链表的插入问题,第一步就是要找到新节点的插入位置。
Node *this,*next,*new_node;
for(this=root;(next=this->fwd)!=NULL;this=next){
if(next->value==new_value)
return 1;
if(next->value > new_value)
break;
}
上面是书中寻找新节点插入位置的代码。程序的意思是把new_node插入到this和next指向的节点中间,但是不插入链表中已存在的数值。
新节点插入位置分析:
1)最一般的情况就是将新值插入到链表的中间了,如下图所示:
插入新节点后的链表示意图如下:
代码实现:
this->fwd=new_node;
new_node->fwd=next;
next->bwd=new_node;
new_node->bwd=this;
分析问题时要注意边界情况,往往这也是特殊情况出现的地方。
2)在链表起始位置插入新节点
插入新节点后的链表示意图如下:
代码实现:
this->fwd=new_node;
new_node->fwd=next;
next->bwd=new_node;
new_node->bwd=NULL;
有一点请务必注意:在这种情况下,有
root=this;
3)在链表的末端插入新节点
![](https://img-my.csdn.net/uploads/201210/30/1351533519_7932.jpg)
插入新节点后的链表示意图如下:
![](https://img-my.csdn.net/uploads/201210/30/1351533555_3694.jpg)
代码实现:
this->fwd=new_node;
new_node->fwd=NULL:
new_node->bwd=this;
root->bwd=new_node;
有一点请务必注意:
在这种情况下,有next=NULL;
4)还有一种更特殊的情况-原链表是空的
![](https://img-my.csdn.net/uploads/201210/30/1351533630_1538.jpg)
插入新节点后的链表示意图如下:
![](https://img-my.csdn.net/uploads/201210/30/1351533658_9008.jpg)
代码实现:
this->fwd=new_node;
new_node->fwd=NULL;
new_node->bwd=NULL;
this->bwd=new_node;
有一点请务必注意:
在这种情况下,有
next=NULL;
this=root;
插入新节点的实现代码
1)自己的代码实现
/*malloc a new node for the would-be insertion */
new_node=(Node *)malloc(sizeof(Node));
if(!new_node){
printf("malloc new node error!\n");
exit(2);
}
new_node->value=new_value;
if(!this->fwd){//the list is empty before insertion
this->fwd=new_node;
new_node->fwd=NULL;
new_node->bwd=NULL;
this->bwd=new_node;
} else if(!next->bwd){//insert to the right position after root
this->fwd=new_node;
new_node->fwd=next;
next->bwd=new_node;
new_node->bwd=NULL;
} else if(!next->fwd){//insert in the end
this->fwd=new_node;
new_node->fwd=NULL:
new_node->bwd=this;
root->bwd=new_node;
} else{//insert in the middle of list
this->fwd=new_node;
new_node->fwd=next;
next->bwd=new_node;
new_node->bwd=this;
}
我自己写的代码也能实现插入新节点的功能,但是呢,太冗余了。
2)书中的代码分析及实现
New node insertion position | Code implementation | notes |
middle | 1) this->fwd=new_node; 2)new_node->fwd=next; 3)next->bwd=new_node; 4)new_node->bwd=this; | |
start | 1)this->fwd=new_node; 2)new_node->fwd=next; 3)next->bwd=new_node; 4)new_node->bwd=NULL; | root=this; |
end | 1)this->fwd=new_node; 2)new_node->fwd=NULL: 3)new_node->bwd=this; 4)root->bwd=new_node; | next=NULL; |
empty list | 1)this->fwd=new_node; 2)new_node->fwd=NULL; 3)new_node->bwd=NULL; 4)this->bwd=new_node; | next=NULL; this=root; |
请仔细观察这个表格:
1.四种情况下第一、第二行代码是完全一样的。
对于end、empty两种情况,请注意notes中next=NULL。
---这个启示我们可以把第一、第二行代码从if、else语句中抽取出来,放在if、else语句之前。
2.empty、end两种情况下的第四行代码是一样的,即this->bwd=new_node,因为在empty情况下this=root;
而middle、start两种情况下第三行代码是一样的,即next->bwd=new_node;
在这儿可以发现:
this->bwd=new_node;
next->bwd=new_node;
区别在于this和next。因此我们寻找一个判别条件来判断到底是取
this->bwd=new_node;还是next->bwd=new_node。
再观察notes列,对于empty、end,next=NULL;而对于middle、start,next!=NULL,
因此next是否等于NULL便是判断条件。
if(next==NULL)
this->bwd=new_node;
else
next->bwd=new_node;
3.对于start的第四行代码和empty的第三行代码是一样的,即new_node->bwd=NULL;
而middle的第四行和end的第三行代码是一样的,即new_node->bwd=this。
观察notes可知this是否等于root便可作为判断条件。
if(root==this)
new_node->bwd=NULL;
else
new_node->bwd=this;
综上所述,完整代码如下:
//the following method is from the book with little change.
int dll_insert(Node *root,int new_value){
Node *this,*next,*new_node;
for(this=root;(next=this->fwd)!=NULL;this=next){
if(next->value==new_value)
return 1;
if(next->value > new_value)
break;
}
new_node=(Node *)malloc(sizeof(Node));
if(!new_node){
printf("malloc new node error!\n");
return -1;
}
new_node->value=new_value;
new_node->fwd=next;
this->fwd=new_node;
if(root==this)
new_node->bwd=NULL;
else
new_node->bwd=this;
if(next==NULL)
root->bwd=new_node;
else
next->bwd=new_node;
root->value++;
return 1;
}
但愿自己表述清楚了,希望自己没有使用错别字。
欢迎大家批评指正!