数据结构与算法-循环链表、双向链表

我们这里接着上一篇单链表继续往下深入学习循环链表、双向链表

🎈3.循环链表

🔭3.1循环链表的概念

循环链表是一种头尾相接的链表。它的特点是由链表的尾结点的指针域不为空,而是指向头结点,整个链表形成一个环。由此,从表中任何一个结点出发均可找到链表的其他结点。在单链表中,将终端结点的指针域NULL改为指向表头结点的或开始结点,就得到了单链形式的循环链表,并简称为单循环链表。与单链表一样,为了使空表和非空表处理一致,循环链表也可以设置一个头结点。空循环链表和非空循环链表如图所示:
在这里插入图片描述

🏆单循环链表的基本算法与非循环单链表的算法基本相同,只需对表尾的判断条件做出修改即可,例如,判断表尾结点的条件为:p->next==head.

🔭3.2循环链表的基本操作

在这里插入图片描述

🔎对于上述这个非空单循环链表:

✅当我们用头指针表示单循环链表:找a1的时间复杂度为:O(1),找an的时间复杂度为:O(n).
✅当我们用尾指针表示单循环链表:找a1的时间复杂度为:O(1)(R->next->next),找an的时间复杂度为:O(1)(R).
🏆因此,我们通常采用尾指针的单循环链表。

🔎3.2.1创建空表

void InitList(LNode *&rear)
{
	rear = new LNode;//创建头结点
	rear->next = rear;
}

🔎3.2.2插入操作

void InsertList(LNode *&rear, ElemType e)
{//在表尾插入一个结点
	LNode* s = new LNode;
	s->data = e;
	s->next = rear->next;
	rear->next = s;
	rear = s;
}

🔎3.2.3删除操作

void Delete(LNode*& rear, ElemType& e)
{//删除链表的第一个结点
	if (rear->next == rear)
		return;//空表,无法删除元素
	LNode* p = rear->next->next;//p指向第一个元素
	e = p->data;
	rear->next->next = p->next;
	if (p == rear)
		rear = p->next;
	delete p;
}

🎈4.双向链表

双向链表是在单链表的每一个结点里再增加一个指向其直接前驱的指针域prior,这样形成的链表有两个方向不同的链,故称为双向链表。
在这里插入图片描述

🔭4.1类型定义

typedef int ElemType;
typedef struct DNode
{
	ElemType data;//存放数据元素信息
	DNode* next;//存放后继结点的地址信息
	DNode* prior;//存放前驱结点的地址信息
}DNode;

🔭4.2头插法创建双向链表

void CreatList_h(DNode*& head, int n)
{
	DNode* s;
	head = new DNode;//创建头结点
	head->next = head->prior = NULL;//前后指针置为NULL
	int i = 0;
	for (i = 0; i < n; i++)//循环创建数据结点
	{
		s = new DNode;
		cin >> s->data;//读入数据
		s->next = head->next;//连接s的两个指针
		s->prior = head;
		if (head->next != NULL)
			head->next->prior = s;
		head->next = s;
	}
}

🔭4.3尾插法创建双向链表

void CreatList_t(DNode*& head, int n)
{
	DNode* s, * rear;
	head = new DNode;//创建头结点
	head->next = head->prior = NULL;//前后指针置为NULL
	rear = head;
	int i = 0;
	for (i = 0; i < n; i++)//循环创建数据结点
	{
		s = new DNode;//创建新的数据结点
		cin >> s->data;//读入数据
		s->prior = rear;//s插在rear后面
		rear->next = s;
		rear = s;
	}
	rear->next = NULL;
}

🔭4.4插入操作

在这里插入图片描述

void InsertDlist(DNode*& head, int i, ElemType e)
{
	int j = 0;//计数器
	DNode* p = head,*s;
	while (p && j < i - 1)
	{
		p = p->next;
		j++;
	}
	if (!p || j > i - 1)
		return;
	else//找到第i-1个结点
	{
		s = new DNode;
		s->data = e;
		s->next = p->next;//p结点后插入s结点
		s->prior = p;
		if (p->next)
			p->next->prior = s;
	}
}

🔭4.5删除操作

DeleteDList()函数在双向链表中,结点的删除操作涉及前驱结点和后继结点两个指针域的变化。设在双向链表中删除指针p所指向结点的后继结点q,需要修改两个指针域,删除操作指针变化过程如图所示:
在这里插入图片描述

void DeleteDList(DNode*& head, int i, ElemType& e)
{
	int j = 0;//计数器
	DNode* p = head, * q;
	while (p->next && j < i - 1)
	{
		p = p->next;
		j++;
	}
	if (!(p->next) || j > i - 1)
		return;
	else
	{
		q = p->next;
		e = q->data;
		p->next = q->next;//删除q所指结点
		if (q->next != NULL)
			q->next->prior = p;
		delete q;
	}
}

好啦,关于循环链表和双向链表的知识点到这里就结束啦,后期会继续更新数据结构与算法的相关知识,欢迎大家持续关注、点赞和评论!❤️❤️❤️

  • 65
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 113
    评论
评论 113
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一口⁵个团子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值