目录
如对您有帮助,还望三连支持,谢谢!!!
之前我们讲过了单链表,单链表的各个节点只有一个next指针,指向该节点的下一个节点。今天我们来讲解一下双向链表。
1.双向链表的结构:
如上图所示,双向链表每个节点有两个指针:next,prev。一个指向下一个节点,另一个指向该节点上一个节点。除此之外和单链表基本相同。
注意:这⾥的“带头”跟前⾯我们说的“头节点”是两个概念,实际前⾯的在单链表阶段称呼不严
谨,但是为了同学们更好的理解就直接称为单链表的头节点。
带头链表⾥的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这⾥“放哨
的”
“哨兵位”存在的意义:
遍历循环链表避免死循环。
2双向链表的实现:
我们之前实现了单向链表的各种功能,其实,双向链表的功能实现起来比单链表更加简单,话不多说,我们开始实现双向链表:
我们依然是创建三个文件,一个头文件:List.h,两个源文件:test.c和List.c。我们之前在单链表专题中讲过这三个文件的作用,这里就不在赘述。
我们先来看一下头文件:
接下来我们就来一个一个实现:
双向链表的初始化:
双向链表初始化就是创建一个哨兵位,注意的是因为双向链表全名是带头双向循环链表,所以phead->next=phead->prev=phead,保证是循环的。其余注意事项我们在单链表中讲过,不再赘述。
链表的销毁:
注意的是while循环的判断条件,因为是循环链表,所以不能像单链表一样判断pcur是不是NULL,当pcur指向phead的时候,证明pcur已经遍历了一次双向链表,此时循环结束,所以循环的判断条件是:pcur!=phead。
链表数据的打印:
循环判断条件和单链表不一样,其余都一样,不再赘述。
申请节点:
申请得到新的节点newnode,与单链表申请节点大同小异,不在讲解。
尾插:
尾插影响到的节点有:头结点,连来的尾节点以及插入的新节点,头结点和新节点都有,所以我们要找到尾节点。
在单链表中,我们通过遍历链表的方式才能找到尾节点,但是在双向链表中尾节点不就是头结点的prev指针指向的节点吗?这就降低了尾插的难度,示意图如下:
修改phead的prev指针,使其指向newnode;修改ptail的next指针使其指向newnode。
修改newnode的prev指针,使其指向ptail;修改newnode的next指针,使其指向phead。
头插:
头插影响的节点有phead和phead->next(也就是tem)和newnode。
我们只需要将phead的next指针指向newnode;tem节点的prev指针指向newnode
将newnode的prev指针指向phead;newndoe的next指针指向tem。
头删:
头删影响的节点有phead和phead->next(也就是del节点)和phead->next->next(也就是tem节点).
只需要把phead的next指针指向tem,tem的prev指针指向phead即可。
同时要注意断言条件与单链表不同,当链表没有有效节点时,此时链表只有一个哨兵位,这种情况不能进行头删操作。
尾删:
尾删影响的节点有phead和phead->prev和phead->prev->prev三个节点。
操作示意图如下:
修改phead的prev指针,使其指向ptail;修改ptail的next指针,使其指向phead。
在pos位置之后插入节点:
在pos之后插入节点,影响到的节点有:pos和newnode和pos->next三个节点。
操作原理和头插大同小异,这里便不再赘述。
删除指定位置节点:
删除指定位置节点,影响的节点有:pos->prev和pos和pos->next这三个节点。
操作步骤与头删尾删大同小异,不在详细讲解。
3.顺序表和链表的优缺点分析:
这以前是一道面试时问的问题,顺序表和链表的优缺点分析,我们学习时要注意知识之间的联系,善于对比挖掘,才能学的更加扎实。
至此,我们双向链表相关内容讲解完毕,希望大家能够有所收获。