【链表-双向链表】

链表-双向链表

  • 1.链表的分类
    • [1.1 分类依据](#1.1 分类依据)
    • [1.2 常用类型](#1.2 常用类型)
  • 2.双向链表的
    • [2.1 双向链表的结构](#2.1 双向链表的结构)
    • [2.2 双向链表的操作](#2.2 双向链表的操作)
      • [2.2.1 **初始化**](#2.2.1 初始化)
      • [2.2.2 **尾插**](#2.2.2 尾插)
      • [2.2.3 **头插**](#2.2.3 头插)
      • [2.2.4 **尾删**](#2.2.4 尾删)
      • [2.2.5 **头删**](#2.2.5 头删)
      • [2.2.6 在pos位置之后插入数据](#2.2.6 在pos位置之后插入数据)
      • [2.2.7 删除pos节点](#2.2.7 删除pos节点)
      • [2.2.8 查找](#2.2.8 查找)
      • [2.2.9 销毁](#2.2.9 销毁)

1.链表的分类

1.1 分类依据

单向or双向

带头or不带头

(一般会称d1为头节点,但实际上head这个哨兵节点才是头节点)

循环or不循环

综上所述,222共有8种链表

1.2 常用类型

我们一般最常用的就是单链表和双链表。
单链表:不带头单向不循环链表

双向链表:带头双向循环链表

2.双向链表的

2.1 双向链表的结构

双向链表包含三个部分,:

1.数据

2.指向下一个节点的指针

3.指向上一个节点的指针

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

ps:单链表和双向链表的初始状态?

单链表为空链表

双向链表剩下一个头结点(即哨兵位)

2.2 双向链表的操作

2.2.1 初始化

方法一

c 复制代码
void LTInit(LTNode** pphead);
c 复制代码
void LTInit(LTNode** pphead)
{
	//给双向链表创建一个哨兵位
	*pphead = LTBuyNode(-1);//哨兵位不存储有效数据
}

方法二

c 复制代码
LTNode* LTInit();
c 复制代码
LTNode* LTInit()
{
	LTNode* phead = LTBuyNode(-1);
	return phead;
}

打印双向链表

c 复制代码
void LTPrint(LTNode* phead);
c 复制代码
void LTPrint(LTNode* phead)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}
c 复制代码
void LTInit(LTNode** pphead)
{
	//给双向链表创建一个哨兵位
	*pphead = LTBuyNode(-1);//哨兵位不存储有效数据
}

2.2.2 尾插

c 复制代码
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
c 复制代码
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);//哨兵位空,那就不是一个有效的双向链表
	LTNode* newnode = LTBuyNode(x);

	//phead phead->prev newnode
	newnode->prev = phead->prev;//新节点头指向原链表的尾节点
	newnode->next = phead;//新节点尾节点指向哨兵位

	phead->prev->next = newnode;//原本的尾节点phead->prev指向新的尾节点
	phead->prev = newnode;//哨兵位指向新的尾节点
}

2.2.3 头插

c 复制代码
void LTPushFront(LTNode* phead, LTDataType x); 
c 复制代码
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);

	//phead newnode phead->next
	newnode->next = phead->next;
	newnode->prev = phead;

	phead->next->prev = newnode;
	phead->next = newnode;
}

2.2.4 尾删

c 复制代码
void LTPopBack(LTNode* phead);
c 复制代码
void LTPopBack(LTNode* phead)
{
	//链表必须有效且链表不能为空(只有一个哨兵位)
	assert(phead && phead->next != phead);

	LTNode* del = phead->prev;
	//phead del->prev del
	del->prev->next = phead;
	phead->prev = del->prev;

	//删除del节点
	free(del);
	del = NULL;
}

2.2.5 头删

c 复制代码
void LTPopFront(LTNode* phead);
c 复制代码
void LTPopFront(LTNode* phead)
{
	assert(phead && phead->next != phead);

	LTNode* del = phead->next;

	//phead del del->next
	phead->next = del->next;
	del->next->prev = phead;

	//删除del节点
	free(del);
	del = NULL;
}

2.2.6 在pos位置之后插入数据

c 复制代码
void LTInsert(LTNode* pos, LTDataType x);
c 复制代码
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* newnode = LTBuyNode(x);
	//pos newnode pos->next
	newnode->next = pos->next;
	newnode->prev = pos;

	pos->next->prev = newnode;
	pos->next = newnode;
}

2.2.7 删除pos节点

c 复制代码
void LTErase(LTNode* pos);
c 复制代码
void LTErase(LTNode* pos)
{
	//pos理论上来说不能为phead,但是没有参数phead,无法增加校验
	assert(pos);
	//pos->prev pos pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

PS:LTErase参数理论上要传二级,因为我们需要让形参的改变影响到实参,但是为了保持接口一致性才传的一级。

2.2.8 查找

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

2.2.9 销毁

c 复制代码
void LTDesTroy(LTNode* phead);
c 复制代码
void LTDesTroy(LTNode* phead)
{
	assert(phead);

	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	//此时pcur指向phead,而phead还没有被销毁
	free(phead);
	phead = NULL;
} 

ps:LTDestroy参数理论上要传二级,因为我们需要让形参的改变影响到实参,但是为了保持接口一致性才传的一级。传一级存在的问题是,当形参phead置为NULL后,实参plist不会被修改为NULL,因此解决办法是:调用完方法后手动将实参置为NULL。

相关推荐
爱吃生蚝的于勒1 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
失落的香蕉4 小时前
C语言串讲-2之指针和结构体
java·c语言·开发语言
ChoSeitaku6 小时前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
DdddJMs__1357 小时前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
娃娃丢没有坏心思7 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++
workflower7 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
一个不喜欢and不会代码的码农8 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode
ahadee9 小时前
蓝桥杯每日真题 - 第11天
c语言·vscode·算法·蓝桥杯
No0d1es9 小时前
2024年9月青少年软件编程(C语言/C++)等级考试试卷(九级)
c语言·数据结构·c++·算法·青少年编程·电子学会
bingw01149 小时前
华为机试HJ42 学英语
数据结构·算法·华为