首页 > 其他分享 >双向链表<数据结构 C版>

双向链表<数据结构 C版>

时间:2024-07-24 22:55:43浏览次数:17  
标签:node 结点 LN next 链表 phead 双向 数据结构

目录

关于链表的分类

 双向链表结构体

初始化

尾插

头插

打印

判断是否为空

尾删

头删

查找

指定位置之后的插入

指定位置的删除

销毁


关于链表的分类

根据链表的三大特性,单向or双向、带头or不带头、循环or不循环,可将链表分为2*2*2,8种链表,前面我们已经实现了单链表,即:不带头单向非循环链表,它的结构简单,不常用于单独存储数据,而是作为其他数据结构的子结构。

实际运用中,只有单链表(不带头单向非循环链表)和双向链表(带头双向循环链表)运用最多,带头双向循环链表,结构最复杂,常常运用于单独存储数据,使用的链表结构也几乎都是双向带头链表。

附上一张bit课件的图:


 双向链表结构体

放一张bi课件的图片很形象:

//重定义一下类型,方便统一修改
typedef int LNDataType;

typedef struct ListNode {
	LNDataType data;//数据
	struct ListNode* prev;//前一个结点
	struct ListNode* next;//后一个结点
}LN;

初始化

双向链表的初始化,应创建一个哨兵结点(也称头结点),它存放的数据是无效数据(假定-1),

所以我们先实现一个创建单节点的函数:

//创建新节点
LN* LNBuyNode(LNDataType x) {
	LN* node = (LN*)malloc(sizeof(LN));//开辟空间
	if (!node) {//判断为空
		perror("malloc fail!");
		exit(1);
	}
	node->data = x;//传入数据
	node->next = node->prev = node;
	//双向循环链表单节点也应满足循环,不能初始化为NULL;
	return node;
}

接着我们就可方便地调用,创建一个哨兵结点:

//初始化
void LNInit(LN** pphead) {
	assert(pphead);
	*pphead=LNBuynode(-1);
}

尾插

//尾插
void LNPushBack(LN* phead, LNDataType x) {
	assert(phead);
	LN* node = LNBuyNode(x);//创建新结点
	node->next = phead;//先对新申请的结点参数进行操作,防止对原链表造成改变
	node->prev = phead->prev;

	phead->prev->next = node;//更改尾结点的next参数的指向
	phead->prev = node;//更改头结点prev结点的指向
}

运行测试一下:


头插

注意头插的操作是在哨兵位后插入,双向链表为空的情况也是只剩下哨兵位,因为哨兵位并没有存储有效数据。

//头插
void LNPushFront(LN* phead, LNDataType x) {
	assert(phead);
	LN* node = LNBuyNode(x);//创建新结点
	node->next = phead->next;//先对新申请的结点参数进行操作,防止对原链表造成改变
	node->prev = phead;

	
	phead->next->prev = node;//更改头结点的下一个结点的prev指向
	phead->next = node;//更改头结点的next指向
}

运行测试一下:


打印

//打印
void LNPrint(LN* phead) {
	assert(phead);
	LN* pcur = phead->next;
	//因为头结点内存储的是无效数据,所以我们让它指向下一个结点
	while (pcur!=phead) {//与头结点相遇说明我们已经遍历完整个链表了
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

判断是否为空

双向链表为空的情况是只有一个哨兵结点而不是NULL

//判断是否为空
bool LNEmpty(LN* phead) {
	assert(phead);
	return phead->next == phead;
}

尾删

//尾删
void LNPopBack(LN* phead) {
	assert(phead);
	assert(!LNEmpty(phead));//删除操作至少有一个有效数据
	//LNEmptys是空返回true,取反保证不为空
	LN* del = phead->prev;//保存要删除的结点
	phead->prev = del->prev;//对要受影响的结点的参数进行更改
	phead->prev->next = phead;

	free(del);//释放掉该地址
	del = NULL;
}

运行测试一下:


头删

//头删
void LNPopFront(LN* phead) {
	assert(phead);
	assert(!LNEmpty(phead));
	LN* del = phead->next;//保存要删除的结点
	phead->next = del->next;//对要受影响的结点的参数进行更改
	phead->next->prev = phead;

	free(del);//释放掉该地址
	del = NULL;
}

运行测试一下:


查找

因为后面涉及到任意位置的操作,所以这里要写一个查找方法:

//查找
LN* LNFind(LN* phead, LNDataType x) {
	assert(phead);
	assert(!LNEmpty(phead));
	LN* pcur = phead->next;
	while (pcur!=phead) {
	//判断条件为!=phead,遇到哨兵位说明已经遍历完
		if (pcur->data == x) {
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

运行测试一下:


指定位置之后的插入

//指定位置之后的插入
void LNInsert(LN* pos, LNDataType x) {
	assert(pos);
	LN* node = LNBuyNode(x);
	node->next = pos->next;//先对要受影响的结点的参数进行更改
	node->prev = pos;

	pos->next->prev = node;//更改pos结点的后一个结点的prev参数
	pos->next = node;//更改pos结点的next参数
}

运行测试一下:


指定位置的删除

//任意位置的删除
void LNErase(LN* pos) {
	assert(pos);
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);
	pos = NULL;
}

运行测试一下:


销毁

//销毁
void LNDestory(LN** phead) {
	assert(phead && *phead);
	LN* pcur = (*phead)->next;
	while (pcur != *phead) {
		LN* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(*phead);
	*phead =pcur= NULL;
}

标签:node,结点,LN,next,链表,phead,双向,数据结构
From: https://blog.csdn.net/2302_80803681/article/details/140593834

相关文章

  • 【数据结构初阶】一篇文章带你超深度理解【单链表】
     hi!目录前言:1、链表的概念和结构2、单链表(SingleList,简写SList)的实现2.1  定义链表(结点)的结构2.2 创建一个链表2.3 打印链表2.4 尾插2.5 头插2.6 尾删2.7 头删2.8 查找2.9 在指定位置之前插入数据2.10 在指定位置之后插入数据2.11......
  • 【数据结构】树和二叉树
    目录1.前言2.树2.1树的概念2.2树中的重要概念2.3树的表示形式 2.4树的应用3.二叉树3.1概念3.2两种特殊的二叉树3.3二叉树的性质3.4二叉树的存储3.5二叉树的遍历方式3.5.1创建二叉树3.5.2二叉树的遍历3.6二叉树的基本操作4.总结1.前言二叉树是数据结构中......
  • 数据结构与算法,剑指Offer 50题
    队列队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。对列的添加insertappend队列的取值列表[-1]列表[0]队列的删......
  • 最全数据结构个人笔记【单向链表】
    1.链表基本概念链式存储的线性表,简称链表链表其实是由一个或者多个结构体通过指针指向的关系构成我们把每个结构体的变量称为节点,节点里面由两个成员组成一个是数据域,另外一个是指针域,指针域是用于存放下一个节点的地址以此类推,我们把这种存储方式称为链式存储2.链......
  • PART1-Oracle关系数据结构-索引和索引组织表
    3.索引组织表3.1.索引概述索引是与表或表簇关联的可选结构,有时可以加快数据访问速度。通过在表的一个或多个列上创建索引,在某些情况下,您可以从表中检索一小部分随机分布的行。索引是减少磁盘I/O的众多方法之一。如果堆组织表没有索引,那么数据库必须执行全表扫描才能找到一个......
  • 数据结构(Java):Map集合&Set集合&哈希表
    目录1、介绍1.1 Map和Set1.2模型2、Map集合2.1Map集合说明2.2 Map.Entry<K,V>2.3Map常用方法2.4Map注意事项及实现类 3、Set集合3.1Set集合说明 3.2 Set常用方法 3.3Set注意事项及其实现类4、TreeMap&TreeSet4.1集合类TreeMap(Key-Value模型)4.1.1底......
  • 【数据结构】:用Java实现链表
    在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。概念顺序表是物理上连续,逻辑上也是连续的链表......
  • 【数据结构】队列详解和模拟实现
    大家好,今天我们学习队列,本篇博客主要涉及一般队列,环形队列和双端队列,每个队列应用场景均有所差异,下面我们一起来看看吧。队列(Queue)一,概念队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性入队列:进行插入操作的一端称为队尾(Ta......
  • 数据结构实验二——单链表的基本操作(2021级zzu)
    ps:滴~打卡第二天,好困啊~~~~~~数据结构实验二——单链表的基本操作一、实验目的二、实验内容(选其中之一写实验报告)三、问题描述四、数据结构定义五、算法思想及算法设计5.1实验内容(1)5.1.1理论实现和代码实现5.2实验内容(2)5.2.1代码实现六、运行示例七、实验代......
  • [杂项] [算法] [数据结构] 暑期专题狂补
    或曰,有学长两天授吾以十专题,吾顿感日月之紧迫,以专题竟不能以吾之所有,遂成此文,以记之语文确实没学好本文可能涵盖多个知识点,故每个的讲解比较简略,仅供参考一.2-SAT$2-SAT$用于求解一个变量只有两种情况的适应性问题(就是有没有解以及输出一种方案);其实可以类比二分图最大......