链表 - 双链表/循环链表/静态链表

双链表

在这里插入图片描述
单链表无法逆向检索,有时候不太方便;
使用双链表,可进可退,存储密度比单链表低一些,因为一个节点除了后继指针next还多了一个前驱指针prior

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;

插入

  1. 一般情况:插入位置在两个节点之间
    Step 1:申请一个新结点存放元素e,此时其前驱后继指针都为空。
    在这里插入图片描述
    Step 2:新结点的后继指针指向其后一个节点(y);后一个节点的前驱指针改为指向新结点(s)。
    在这里插入图片描述
    Step 3:新结点的前驱指针指向其前面的一个节点(x);前面一个节点的后继指针改为指向新结点(s)。
    在这里插入图片描述
//将节点*s插入到节点*p之后
bool InsertNextDNode(DNode *p, DNode *s) {
	s->next = p->next;
	p->next->prior = s;
	s->prior = p;
	p->next = s;
}
  1. 特殊情况:插入为末尾位置,无后继节点
    插入后新结点后继指针为空
    在这里插入图片描述
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;
}

删除

若要删除一个节点,需要对其前后节点的指针进行处理才可删除,否则会发生错误。

  1. 一般情况:需要删除的节点在两个节点之间
    Step 1:将该节点的前驱节点(x)的后继指针指向该节点的后继节点(y);
    Step 2:将该节点的后继节点(y)的前驱指针指向该节点的前驱节点(x);
    在这里插入图片描述
    Step 3:释放该节点内存,删除
  2. 考虑特殊情况
    删除p节点的后继节点
    Step 1: 判断p节点合法,不为空;不合法,直接返回;
    Step 2:取p节点的后继指针(找到p节点的后继节点);
    Step 3:若p节点的后继指针指向的内存单元不为空NULL(p有后继节点),按照一般情况处理;否则,当没有后继节点时,直接返回;
    Step 4:释放后继节点q,完成删除操作
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;
}

双链表的遍历

  1. 后向遍历
while(p != NULL) {
	p = p->next;
}
  1. 前向遍历
while(p != NULL) {
	p = p->prior;
}
  1. 前向遍历(跳过头结点)
while(p->prior != NULL) {
	p = p->prior;
}

双链表不可随机存取,按位查找、按值查找操作都只能用遍历的方式 实现。时间复杂度为 O ( n ) O(n) O(n)

循环链表

分为循环单链表循环双链表

  1. 循环单链表
    单链表:表尾节点的next指针指向NULL;
    从一个节点出发只能找到后续的各个节点
    在这里插入图片描述
    循环单链表:表尾节点的next指针指向头结点;
    从一个节点出发可以找到其他任何一个节点;
    很多时候对链表的操作都是在头部或尾部;
    从头结点找到尾部的时间复杂度为 O ( n ) O(n) O(n),从尾部找到头部的时间复杂度为 O ( 1 ) O(1) O(1)
    在这里插入图片描述
    单链表的初始化:
bool InitList(LinkList &L) {
	L = (LNode *)malloc(sizeof(LNode));
	if (L == NULL)
		return false;
	L->next = L;	//头结点next指针指向头结点
	return true;
}

如何判断循环单链表为空:
当头结点的next指针指向头结点时
在这里插入图片描述

bool Empty(LinkList L) {
	if(L->next == L)
		return true;
	else
		return false;
}

如何判断节点p是否为循环单链表的表尾节点

bool isTail(LinkList L, LNode *p) {
	if(p->next == L)
		return true;
	else
		return false;
}
  1. 循环双链表
    双链表:表头结点的prior指向NULL,表尾结点的next指向NULL
    在这里插入图片描述
    循环双链表:表头结点的prior指向表尾结点,表尾结点的next指向头结点
    在这里插入图片描述
    循环双链表的初始化
bool InitLinkList(DLinklist &L) {
	L = (DNode *)malloc(sizeof(DNode));
	if (L == NULL)
		return false;
	L->prior = L;	//头结点的prior指向头结点
	L->next = L;	//头结点的next指向头结点
	return true;
}

如何判断循环双链表为空:
当头结点的next和prior指针都指向头结点时,在判断时可以治判断next指针是否指向头结点
与循环单链表判断方式相同
在这里插入图片描述

bool Empty(DLinklist L) {
	if(L->next == L)
		return true;
	else
		return false;
}

如何判断节点p是否为循环双链表的表尾节点:
与循环单链表判断方式相同

bool isTail(DLinkList L, DNode *p) {
	if(p->next == L)
		return true;
	else
		return false;
}

插入

在p结点之后插入s结点

bool InsertNextDNode(DNode *p, DNode *s) {
	s->next = p->next;
	p->next->prior = s;
	s->prior = p;
	p->next = s;
}

删除

删除p的后继节点q

p->next = q->next;
q->next->prior = p;
free(q);

静态链表

优点:增、删操作不需要大量移动元素;
缺点:不能随机存取,只能从头结点开始依次往后查找,容量固定不可变
使用场景: 不支持指针的低级语言;数据元素数量固定不变的场景(如操作系统的文件分配表FAT)
在这里插入图片描述
静态链表:分配一整片连续的内存空间,各个节点集中安置
游标充当指针
每个数据元素4B,每个游标4B(每个节点共占8B
设起始地址为addr
e 1 e_1 e1的存放地址为 addr + 8*2
在这里插入图片描述

定义静态链表

#define MaxSize 10	//静态链表的最大长度
typedef struct Node{		//静态李娜表结构类型的定义
	ElemType data;	//存储数据元素
	int next;		//下一个元素的数组下标
} SLinkList[MaxSize];

//声明
void testSLinkList() {
	SLinkList a;
}

基本操作

  1. 初始化
    把a[0]的next设为-1,不指向任何元素;
    可在初始化时,让next为某个特殊值,如-2
    在这里插入图片描述
  2. 查找
    从头结点出发挨个往后遍历节点,时间复杂度为 O ( n ) O(n) O(n)
  3. 插入位序为i的节点:
    step 1:找到一个空的节点,存入数据元素;
    step 2:从头结点出发找到位序为i-1的结点
    step 3:修改新结点的next
    step 4:修改i-1号结点的next

如何判断节点是否为空?可在初始化时,让next为某个特殊值,如-2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值