双向链表
双向链表的定义
单链表的缺点
单链表结点只有后继指针,找后继结点十分方便,它的时间复杂度是O(1),但是找前驱结点十分的复杂,找前驱结点的算法:
int ListLocationHNode(LinkList &L,int checkdata)
Lnode *p;
if(!L) return ERROR; //当是空表的时候,直接返回
p=L //将指针指向头结点
while(p->next->data!=checkdata&&p->next)
p=p->next;
if(!p->next)
return ERROR; //如果寻找到最后一个结点都没有找到返回ERROR
return p->data; //返回上一个结点数据data
很显然它的时间复杂度为O(n),为了解决这个问题,将引入双向链表的线性表
定义
双向链表有两个指针域,prior指向上一个结点,next指向下一个结点
C语言的定义
typedef struct DuLNode {
int data;
struct DuLNode *prior,*next;
}DuLNode,*DuLinkList;
双向循环链表
- 最后一个结点的next指向第一个结点
- 第一个结点的prior指向最后一个结点
- 空表的形式:prior和next都指向自己
双向链表的特点
具有对称性,即p->prior->next=p=p->next->prior
双向链表的一些基本操作:
初始化双向链表
void createDuLinkList(DuLinkList& L, int n) {
L = (DuLNode*)malloc(sizeof(DuLNode));
L->prior = L;
L->next = L;
DuLNode* pHead = L;
for (int i = n; i > 0; i--) {
DuLNode * p = (DuLNode*)malloc(sizeof(DuLNode));
printf("input data:");
scanf("%d", &(p->data));
p->prior = pHead;
p->next = NULL;
pHead->next = p;
pHead = pHead->next;
}
}
遍历双向链表
void ergodicDuLinkList(DuLinkList& L) {
DuLNode* p;
p = L->next;
while (p) {
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
按照索引查找元素结点的地址
DuLNode* GetElemAdoptIndex(DuLinkList& L, int index) {
DuLNode* p = L->next;
int i = 1;
while (p && i < index) {
i++;
p = p->next;
}
if (!p || i > index) {
printf("返回空指针");
return NULL;
}
else
return p;
}
插入元素双向链表
int ListInsert_DuL(DuLinkList& L, int index) {
DuLNode* p;
if (!(p = GetElemAdoptIndex(L, index)))
return ERROR;
DuLNode* s = (DuLNode*)malloc(sizeof(DuLNode));
printf("请输入新结点的data:");
scanf("%d", &(s->data));
s->next = p;
s->prior = p->prior;
p->prior->next =s;
p->prior = s;
return OK;
}
删除元素双向链表
int ListDelete_DuL(DuLinkList& L, int index) {
DuLNode* p;
if (!(p = GetElemAdoptIndex(L, index)))
return ERROR;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return OK;
}
各种线性表的比较
查找首元结点 查找尾结点 查找p的前一个结点
带头结点的单链表 L->next:O(1) 遍历L:O(n) 记录p的位置,重新遍历
带头结点设置头指针L的循环链表 L->next:O(1) 遍历L:O(n) 通过p->next遍历:O(n)
带头结点设置尾指针R的循环链表 R->next->next:O(1) R:O(1) 通过p->next遍历:O(n)
带头结点的双向循环链表 L->next:O(1) L->prior:(O) p->prior:O(1)
顺序存储结构与链式存储结构的比较
链式存储结构的优点:
- 链表可以动态的生成和释放
- 插入删除不需要大量移动元素
链式存储结构的缺点
- 存储密度小 存储密度=数据结点空间/结点全部空间
- 链式存储结构是非随机存储的,增加了时间复杂度
比较
-
从空间上来看,
- 顺序存储的空间是定长的,会出现空间闲置或者溢出的现象,链式存储能够动态的新增和释放结点,所以没有这种情况
- 顺序存储的空间密度是1,不会有其他的开销,链式存储有指针域,空间密度<1,有格外的空间开销
-
从时间上来看
- 顺序存储是随机存储,时间复杂度为O(1),链式存储是非随机存储,时间复杂度为O(n)
- 顺序存储插入删除需要移动大量元素,时间复杂度为O(1),链式存储不需要,时间复杂度为O(n)
-
使用条件
- 顺序表:
- 表长变化不大或能够确定表长
- 不需要频繁的进行插入或者删除的操作
- 经常按位置访问数据
- 链表
- 长度变化大
- 频繁的进行插入和删除操作
- 顺序表: