您的位置:首页 > 编程语言 > C语言/C++

C语言 有序双链表实现插入、删除、打印(正反)等简单操作

2018-01-12 22:45 423 查看
1.双链表与单链表的区别主要是在于双链表中,每个节点都包含两个指针——指向前一个节点的指针,和指向后一个节点的指针。这就便于我们从任何方向遍历整个链表。

下面是节点类型的说明:

typedef struct NODE{
struct NODE *fwd;
struct NODE *bwd;
int value;
}Node;

构造了一个简单的链表节点,此时,我们需要两个根指针,一个指向链表的第一个节点,另一个指向链表的最后一个节点。
我们可以考虑为根指针声明一个完整的节点,但应该注意其value字段不被使用,我们仅仅是用该节点来表示根指针,其并不存在于链表之中,我们让rootp->fwd表示链表第一个节点,而rootp->指向链表最后一个节点,这就需要在main函数中为该节点分配空间,且初始化rootp->fwd以及rootp->bwd为NULL。

Node *rootp;
rootp=(Node *)malloc(sizeof(Node)); //为根指针分配空间,并初始化链表第一部分为NULL
rootp->bwd=NULL;
rootp->fwd=NULL;

这时可以在专门的free函数中释放该空间,也可以在main函数最后释放。

2.实现值的有序插入

这时需要考虑节点插入的位置,一般来讲有4种情况:

1.插入位置在链表中间段;

2.插入位置在链表初始段;

3.插入位置在链表尾部;

4.该链表为空链表;

需要分别考虑这4种不同情况下节点的fwd及bwd指针指向。

以下代码详细描述了这四种情况下的指针指向:

if(next!=NULL)              //四种情况
{                             //1.中间 2.起始 3.末尾 4.空
if(this!=rootp)           //1.中间
{
newnode->fwd=next;
this->fwd=newnode;
next->bwd=newnode;
newnode->bwd=this;
}
else{                   //2.初始
newnode->fwd=next;
rootp->fwd=newnode;
newnode->bwd=NULL;
next->bwd=newnode;
}
}
else{
if(this!=rootp)         //末尾非空
{
newnode->fwd=next;
this->fwd=newnode;
newnode->bwd=this;
rootp->bwd=newnode;
}
else{                   //空
newnode->fwd=NULL;
rootp->fwd=newnode;
newnode->bwd=NULL;
rootp->bwd=newnode;
}
}


同时,我们可以注意到以上单纯实现插入的代码段比较冗余,由于if语句内有功能相似或相同的代码,我们可以将其提取出现,减少代码量。

比如每个if语句中都有newnode->fwd=next,以及this->fwd=newnode.且在程序运行到该段时,newnode及this指针一定不为NULL,故可以直接提取出来。如下是改进后的完整的插入节点的代码段。

ps:注意到根指针rootp的存储空间是在main函数中通过malloc语句分配,存储在堆(heap)中,在做插入、删除等系列操作时,并不需要改变rootp的值,故在函数参数列表中并不需要像单链表头指针一样传入其地址(当然,传地址也是可以的)。

这里是直接将rootp的值传入到函数中。

int dll_insert(Node *rootp,int num)
{
Node *next;     //插入位置的下一个
Node *this;    //插入位置的前一个
Node *newnode;

//	for(this=rootp;(next=this->fwd)!=NULL;this=next)
for(this=rootp,next=rootp->fwd;next;this=next,next=next->fwd) //寻找插入位置,从头开始
{
if(next->value==num)   //插入不同数
return 0;
else if(next->value>num)  //一旦找到比待插入数大的位置,跳出循环;
break;
}

newnode=(Node *)malloc(sizeof(Node)); //为新插入的节点分配空间
if(newnode==NULL)
return -1;
newnode->value=num;
newnode->fwd=next;        //以下是实现插入(简化版)
this->fwd=newnode;

if(this!=rootp)          //非初始
{
newnode->bwd=this;
}
else{                   //初始
newnode->bwd=NULL;
}
if(next!=NULL)          //非结尾
{
next->bwd=newnode;
}
else{                   //结尾(新加入的节点在最后一个位置)
rootp->bwd=newnode;
}

return 1;
}


3.删除节点

在删除某个节点时,需要注意删除的位置是在初始位置,中间位置,还是末尾位置,同时需注意删除之后链表是否为空。

void reduce(Node *rootp)  //删除
{
Node *q,*p,*t;   //t可以木有√
int isFound=0;  //标记是否找到
int num;
puts("Please enter the num to delete:");
scanf("%d",&num);
while(getchar()!='\n')
continue;

for(q=NULL,p=rootp->fwd;p;q=p,p=p->fwd)  //删除
{
if(p->value==num)   //如果找到了
{
isFound=1;
if(q)
{
q->fwd=p->fwd; //
if(p->fwd==NULL) //最后位置
{
rootp->bwd=q; //表示q位置为最后一个,删除p;
}
else{
//	t=p->fwd; //p->fwd->bwd=q;
p->fwd->bwd=q;   //不是最后一个,删除p,p的后一个的前一个为q
}
}
else{
rootp->fwd=p->fwd;  //q为NULL,那么p是第一个,删除p,根指针指向第二个(可能为空,这样整个链表就是空的了),现在是第一个了
if(p->fwd==NULL)  //p是最后一个。说明删除之后就没有了
rootp->bwd=NULL;  //整个链表为空, rootp->bwd=NULL   rootp->fwd=p->fwd,而p->fwd为空
else{
p->fwd->bwd=NULL;//这个意思是p后面还有,那么该节点为第一个了,其bwd要指向NULL;
}

}
free(p);
break;
}
}
if(!isFound)
{
printf("Not found.\n");
}
}

3.正向打印及反向打印

这个操作比较简单,不多做介绍。

void print(Node *rootp)  //打印
{
Node *p;
for(p=rootp->fwd;p;p=p->fwd)  //正向打印,以根指针的初始指向为开始,以链表初始节点开始
{
printf("The num is %d.\n",p->value);
}
}

void bkprint(Node *rootp)  //反向打印
{
Node *p;
for(p=rootp->bwd;p;p=p->bwd)  //反向打印,以根指针的bwd为始,指向的是链表最后一个节点
{
printf("The num is %d.\n",p->value);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 链表
相关文章推荐