数据结构之双向链表的实现(C语言)

概念

在单链表中有了next指针,这就使得我们要查找的下一结点的时间复杂度为O(1)。可是如果我们要查找的是上一结点的话,那么最坏的时间复杂度就是O(n),因此我们每次都要从头遍历开始查找。
为了克服单向性这一缺点,设计出了双向链表(double linked list)是在单链表的每个结点中,在设置一个指向其前驱结点的指针域。 所以在双链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。

//线性表的双向链表的存储结构
typedef struct DulNode
{
	ElemType data;
	struct DulNode *prior;  //直接前驱指针
	struct DulNode *next;	//直接后驱指针
}DulNode,*DuLinkList;

既然单链表也可以有循环链表,那么双链表也可以是循环链表。

双向链表的循环带头结点的空链表如下图。
在这里插入图片描述
非空的循环的带头结点的双向链表如下图
在这里插入图片描述
由于这是双向链表,那么对于链表中每一个结点p,它的后继的前驱是谁?当然还是他自己,它的前驱的后继自然也自己。

p->next->prior = p = p=->prior->next

双向链表是单链表中扩展出来的结构,所以它的很多操作是和单链表相同的,比如求长度,查找元素,获得元素的位置等。这些操作都只要涉及一个方向的指针即可。

插入元素

插入的操作,其实并不复杂,不过顺序很重要。

假设存储元素e的结点为s,要实现将结点s插入到结点p和p->next之间需要下面几步:
在这里插入图片描述

s->prior = p;        //把p赋值给s的前驱
s->next = p->next;   //把p->next赋值非s的后继
p->next->prior =s;   //把s赋值给p->next的前驱
p->next =s;          //把s赋值非p的后继

关键在于他们的顺序,由于第二步和第三步都用到了p->next。如果第四步先执行,则会使p->next提前变成了s,使得插入的工作完不成。所以我们不妨把上面的图的基础上理解,顺序是先搞定s的前驱和后继,在搞定后结点的前驱,最后解决前结点的后继。

删除操作

如果插入操作理解了,那么删除操作,就很简单。
若要删除结点p,只需要下面的两个步骤:
在这里插入图片描述

p->prior->next=p->next;			//把p->next赋值给p->prior的后继
p->next->prior=p->prior;		//把p->prior赋值给p-》next的后继
free(p);                       //释放结点

总结

双向链表相对于单链表来说,要更复杂一些,毕竟它多了一个prior指针,对于插入和删除时,需要注意。另外他由于每个结点都需要记录两份指针,所以在空间上占用多一点,不过,由于它良好的对称性。使得对某个结点的前后结点的操作,带来了方便,可以有效提高算法的时间性能,说简单点,就是用空间来换取时间。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构 课程设计 void main() { list *head=NULL;//头指针初始化 FILE *fp;//定义文件指针 int m;//功能代号吗 do{ printf("\n #############################请选择################################\n"); printf(" # 1、信息输入 "); printf(" 2、显示信息 #\n"); printf(" # 3、查找姓名 "); printf(" 4、删除信息 #\n"); printf(" # 5、修改信息 "); printf(" 6、打开通讯录 #\n"); printf(" # 7、添加信息 "); printf(" 8、 保存 #\n"); printf(" # 9、清屏 "); printf(" 0、退出 #"); printf("\n ###################################################################\n"); printf(" 提示:信息输入和添加信息时最好存入字符,否则,在打开存储文件时,可能会出现乱码!\n"); printf("==输入要执行的功能代号:_"); scanf("%d",&m); switch(m) { case 1:enter(head);break; case 2:display(head); break; case 3:search(head); break; case 4:delete_name(head);break; case 5:change(head);break; case 6:load(head);break; case 7:insert(head);break; case 8:save(head,fp);printf("==保存成功==\n");break; case 9:system("cls");break; } } while(m!=0); } 2、信息输入: void enter(list *&head) { list *p0,*p1,*p2;//定义指针 int m;//为判断是否继续新建的条件 FILE *fp; p0=(list *)malloc(LEN); //开辟一个新单元 p0->next=NULL;//结点指针域为空 head=p0;//第一个结点,即为头结点 printf("请输入信息建立通讯录:\n"); getchar(); scanfdata(p0);//输入数据 p2=p0; printf("是否继续按1输入,按0结束_"); scanf("%d",&m); while(m) { getchar(); p1=(list *)malloc(LEN); //开辟一个新单元 p1->next=NULL; p2->next=p1; scanfdata(p1);//输入数据 p2=p1; head->n = head->n+1;//表长 printf("是否继续按1输入,按0结束_"); scanf("%d",&m); } if(m==0) { save(head,fp);//把信息存到相应文件 } } 3、显示信息 void display(list *head) { list *p; //定义移动指针 int i; char *menu[]={"姓名","城市","电话", "QQ"}; p=head; printf("--------------------------------------------------------------------\n"); for(i=0;i<4;i++) printf("%-20s",menu[i]); printf("\n"); if (head!=N

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值