(数据结构)线性表(下):链表分类及双向链表的实现

链表分类及双向链表的实现

链表的分类

一共有22 2=8种链表

next:指向下一个结点(后继结点)

prev:指向前一个结点(前驱结点)

头结点不存储数据,作为"哨兵位"。

(单链表的第一个结点不能叫头结点!因为其存储了数据!)

不循环链表的尾结点next为空,循环链表的尾结点next不为空

  • 虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:单链表(单向不带头不循环链表)和双向链表(双向带头循环链表)
  1. 单链表:结构简单,⼀般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
  2. 双向链表:结构最复杂,⼀般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。

双向链表

结点结构

c 复制代码
struct ListNode
{
	int data;
	struct ListNode* next;
	struct ListNode* prev;
}

链表结构

双向带头循环链表:

特点:

  1. 头结点的prev指向尾结点,尾结点的next指向头结点。表达为:ptail=phead->prev phead=ptail->next
  2. 循环链表的特性:如果遍历条件是pcur!=NULL,链表将无线循环遍历下去。
  3. phead(即双向链表头结点的地址)永远不会发生改变。(但是头结点中的成员是可以改变的!)

初始化

双向链表为空的情况下只有一个哨兵位,且next和prev指针都指向自己。

(如果连哨兵位都没有的话,这就是单链表而不是双向链表)

c 复制代码
//形成一个新的结点,频繁使用,独立出来
LTNode* buyNode(LTDataType x) {
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL) {
		perror("malloc fail!");
		exit(1);
	}
	node->data = x;
	node->next = node->prev = node;
	return node;
}
/双向链表的初始化法1
void LTInit01(LTNode** pphead) {
	//只有传地址才能真正改变phead!
	*pphead = buyNode(-1);//随便给一个data就行了,因为头结点的数据是不会用到了
}
//双向链表的初始化法2
LTNode* LTInit02() {
	LTNode* phead = buyNode(-1);
	return phead;
}

遍历/打印

双向链表是循环链表,所以循环条件不能再是pcur!=NULL

c 复制代码
//打印双向链表
void LTPrint(LTNode* phead)
{
	LTNode* pcur = phead->next;
	while (pcur != phead) {
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
}

尾插

添加元素时,需要先分情况为链表为空和链表非空,因为有可能操作是不同的。(但是尾插的情况下操作相同就可以合并)

  • 修改顺序?------修改指针指向时,先修改对原链表无影响的指针
  1. newnode的prev、next可以先定
  2. 再修改尾结点的next和头结点的prev
c 复制代码
//尾插
//传入的phead结点不会发生改变,参数就只传一级指针足够
//如果传入的phead指针需要发生改变(LTInit01),参数就需要传二级
void LTPushBack(LTNode* phead, LTDataType x) {
	//如果传入的phead为空,则传入的根本不是双向链表,直接断言报错
	assert(phead);
	LTNode* newnode = buyNode(x);
	newnode->next = phead;
	LTNode* ptail = phead->prev;
	newnode->prev = ptail;
	ptail->next = newnode;
	phead->prev = newnode;
}

头插

头插 是指插入一个结点作为链表的第一个有效节点

当然不能插在phead前面!因为我们知道phead->prev其实是尾结点。

  • 与尾插相同,修改指针指向时,先修改对原链表无影响的指针
c 复制代码
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	LTNode* newnode = buyNode(x);
	newnode->prev = phead;
	newnode->next = phead->next;
	phead->next->prev = newnode;
	phead->next = newnode;
}

尾删

进行删除前一定要先探空------防止导致头结点被删除导致链表销毁等异常问题

//尾删

c 复制代码
void LTPopBack(LTNode* phead)
{
	assert(!LTEmpty(phead));
	LTNode* del = phead->prev;//把尾结点(即要删除的结点)单独写出来避免代码难读
	del->prev->next = phead;
	phead->prev = del->prev;
	free(del);
}

头删

c 复制代码
void LTPushFront(LTNode* phead, LTDataType x)
{
	LTNode* newnode = buyNode(x);
	newnode->prev = phead;
	newnode->next = phead->next;
	phead->next->prev = newnode;
	phead->next = newnode;
}

查找

c 复制代码
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next; 
	}
	return NULL;
}

在pos位置之后插入

c 复制代码
//在pos位置之后插入
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = buyNode(x);
	newnode->prev = pos;
	newnode->next = pos->next;
	pos->next->prev = newnode;
	pos->next = newnode;
}

删除pos位置的结点

c 复制代码
//删除pos位置的结点
void LTErase(LTNode* pos, LTDataType x)
{
	assert(pos);
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);
	pos = NULL;
}

销毁双向链表

c 复制代码
//销毁01 因为需要释放头结点,所以需要传二级指针
void LTDesTroy(LTNode** pphead)
{
	LTNode* pcur = (*pphead)->next;
	while (pcur != *pphead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(*pphead);
	*pphead = NULL;
}
c 复制代码
//销毁02 为保持接口一致性,建议统一参数指针都为一级
//但是需要手动把phead实参置空,否则本函数里只能改变形参
void LTDesTroy(LTNode* phead)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(phead);
}

顺序表与链表的分析(线性表)

相关推荐
晨非辰5 小时前
【面试高频数据结构(四)】--《从单链到双链的进阶,读懂“双向奔赴”的算法之美与效率权衡》
java·数据结构·c++·人工智能·算法·机器学习·面试
im_AMBER5 小时前
数据结构 03 栈和队列
数据结构·学习·算法
卿言卿语7 小时前
CC1-二叉树的最小深度
java·数据结构·算法·leetcode·职场和发展
仰泳的熊猫7 小时前
LeetCode:889. 根据前序和后序遍历构造二叉树
数据结构·c++·算法
Emilia486.8 小时前
【Leetcode&nowcode&数据结构】单链表的应用(初阶)
c语言·数据结构·算法·leetcode
仰泳的熊猫9 小时前
LeetCode:700. 二叉搜索树中的搜索
数据结构·c++·算法·leetcode
杰 .10 小时前
C++ Hash
数据结构·c++·哈希算法
韧竹、10 小时前
数据结构之单链表
数据结构·链表
And_Ii10 小时前
LeetCode 3350. 检测相邻递增子数组 II
数据结构·算法·leetcode