目录
一、定义
在单链表的基础上增加了一个前驱指针prior。
二、特点
- 克服了单链表单向性的缺点(缺点具体指:在单链表中,查找直接后继的执行时间为O(1),而查找直接前驱的执行时间为O(n)。)
- 可进可退(可以向前进行操作,也可以向后进行操作),存储密度比单链表低
三、双链表的实现
1、双链表的定义
typedef struct DNode //定义双链表节点类型
{
ElemType data; //数据域
struct DNode* prior, * next;//前驱和后继指针
}DNode,*DLinkList;
2、双链表的初始化(带头结点)
#include<stdio.h>
#include<stdlib.h>
typedef struct DNode //定义双链表节点类型
{
ElemType data; //数据域
struct DNode* prior, * next;//前驱和后继指针
}DNode, * DLinkList;
//初始化双链表
bool InitDLinkList(DLinkList &L)
{
L = (DNode*)malloc(sizeof(DNode));//分配一个头节点
if (L == NULL) //内存不足,分配失败
{
return false;
}
L->prior = NULL; //头结点的prior永远指向NULL
L->next = NULL; //头节点之后暂时还没有节点
return true;
}
int main()
{
DLinkList L;
InitDLinkList(L);//初始化双链表
//......后续操作......
return 0;
}
3、双链表的判空
//判断双链表是否为空(带头结点)
bool Empty(DLinkList L)
{
if (L->next == NULL)//如果头节点之后没有节点
{
return true; //双链表是空的
}
else
{
return false; //否则不是空的
}
}
4、双链表的插入
过程演示:(在p结点之后插入s结点)
第一步:将s节点的next指针指向p节点的下一个节点
第二步:将p节点的后继节点的前驱指针指向新插入的s节点
第三步:将s节点的前驱指针指向p节点
第四步:将p节点的next指针指向s节点
//在p结点之后插入s结点
bool InsertNextDNode(DNode* p, DNode*s)
{
if (p == NULL || s == NULL) //非法参数
{
return false;
}
s->next = p->next;
if (p->next != NULL) //如果p结点有后继结点
{
p->next->prior = s;
}
s->prior = p;
p->next = s;
return true;
}
修改指针时要注意顺序!!!
双链表的插入实际上是后插操作,那么用后插操作实现双链表节点的插入有什么好处?
- 进行按位序插入操作时只需要找到该位序的前驱节点,然后对该前驱节点进行后插操作即可
- 由于双链表具有前驱指针,所以在进行前插操作时,很容易找到给定节点的前驱节点,然后对前驱节点进行后插操作即可
5、双链表的删除
过程演示:(假设删除指定节点p的后继节点q)
第一步:将p的next指针指向q的后继节点
第二步:将q节点的后继节点的前驱指针指向p节点
第三步:释放q节点
//删除p节点的后继节点
bool DeleteNextDNode(DNode* p)
{
if (p == NULL)
{
return false;
}
DNode* q = p->next; //找到p的后继节点q
if (q == NULL)
{
return false; //p没有后继
}
p->next = q->next;
if (q->next != NULL) //q节点不是最后一个节点
{
q->next->prior = p;
}
free(q); //释放节点空间
return true;
}
销毁双链表:
//销毁双链表
void DestoryList(DLinkList& L)
{
//循环释放各个数据节点
while (L->next != NULL)
{
DeleteNextDNode(L);
}
free(L); //释放头节点
L = NULL; //头指针指向空
}
6、双链表的遍历
后向遍历
//后向遍历
while (p != NULL)
{
//对节点p做相应处理,如打印
p = p->next;
}
前向遍历
//前向遍历
while (p != NULL)
{
//对节点p做相应处理,如打印
p = p->prior;
}
前向遍历(跳过头节点)
//前向遍历(跳过头节点)
while (p->prior != NULL)
{
//对节点p做相应处理,如打印
p = p->prior;
}
双链表不可随机存取,按位查找、按值查找操作都只能用遍历的方式实现。时间复杂度O(n)