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

xiaoxiao2021-02-28  3

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); } }
转载请注明原文地址: https://www.6miu.com/read-1600314.html

最新回复(0)