线性表之双向链表

双向链表
双向链表的定义
单链表的缺点

单链表结点只有后继指针,找后继结点十分方便,它的时间复杂度是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)
  • 使用条件

    • 顺序表:
      • 表长变化不大或能够确定表长
      • 不需要频繁的进行插入或者删除的操作
      • 经常按位置访问数据
    • 链表
      • 长度变化大
      • 频繁的进行插入和删除操作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值