【数据结构和算法初阶(C语言)】双向循环带头链表的增删查改详解(天才设计的链表结构,应用简单逆天!!!!!)

news2024/11/19 7:49:49

目录

​编辑​编辑

1.双向链表的定义:前赴后继 

2.带头链表的定义-----哨兵位 

3.增删查改

3.1创建新节点函数----方便后续增加节点调用

3.2创建哨兵位----创建头结点 

3.3增加节点,尾部插入数据 

3.4尾删除 

3.5查找函数----遍历+对比,返回节点地址 

3.6头删函数

3.7头插函数 

3.8 计算数组的长度

3.9在任意位置pos前插入 

3.10在任意位置删除

3.11链表的销毁 

 4、结语及整个源码含测试用例


1.双向链表的定义:前赴后继 

typedef int LTDataTYpe;
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataTYpe data;
}LTNode;

2.带头链表的定义-----哨兵位 

3.增删查改

先写一个打印函数来方便后续测试:

 

void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur= phead->next;
	while (cur != phead)
	{
		//printf("phead");
		printf("%d<=>", cur->data);
		cur = cur->next;

	}

}

3.1创建新节点函数----方便后续增加节点调用

申请节点,将前后指针置空同时可以给节点赋值,返回这个节点的地址,也就是指向这个节点的指针

LTNode* BuyNode(LTDataTYpe x)
{
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	if (phead == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	phead->data = x;
	phead->next = NULL;
	phead->prev = NULL;
	return phead;
}

3.2创建哨兵位----创建头结点 

后续所有节点可以增加在哨兵位的前后就对应前插、尾插。

最开始的哨兵位前后指针都应该指向自己。

LTNode* LTInit()
{
	
	LTNode* phead = BuyNode(-1);//首先有一个节点
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

3.3增加节点,尾部插入数据 

两种情况一样:

 


void LTPushBack(LTNode* phead, LTDataTYpe x)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* newnode = BuyNode(x);
	newnode->prev = tail;
	tail->next = newnode;
	newnode->next = phead;
	phead->prev = newnode;
	
}

3.4尾删除 


void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;
	free(tail);
	tail->next = phead;
	phead->prev = tailprev;
}

3.5查找函数----遍历+对比,返回节点地址 

LTNode* LTFind(LTNode* phead, LTDataTYpe x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur!= phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

3.6头删函数

void LTPodFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* after = phead->next;
	LTNode* tail = after->next;
	phead->next = tail;
	tail->prev = phead;
	free(after);
}

3.7头插函数 

 

void LTPushFront(LTNode* phead, LTDataTYpe x)
{
	assert(phead);
	LTNode* d1 = phead->next;
	LTNode* newnode = BuyNode(x);
	phead->next = newnode;
	newnode->next = d1;
	d1->prev = newnode;
	newnode->prev = phead;

}

 

3.8 计算数组的长度

 这里对于大家可能有一个误区,我们的头结点也就是哨兵位,数据领域没有保存值,就会有伙伴像在数据域保存链表的大小,增加就+1,减少就-1,但是这种情况只针对数据域是整型的情况,如果数据领域是浮点数或者字符型,加1可能就导致数据不准确,

方法:遍历+计数

int LTSize(LTNode* phead)
{
	assert(phead);//不能是空指针
	int size = 0;
	LTNode* cur = phead->next;//定义遍历指针
	while (cur != phead)
	{
		size++;
		cur = cur->next;

	}
	return size;
}

3.9在任意位置pos前插入 

如果pos=head就是尾插

如果pos = head->next就是头插

void LTInsert(LTNode* pos, LTDataTYpe x)
{
	//首先要查找的节点不能为空
	assert(pos);
	LTNode* newnode = BuyNode(x);
	LTNode* prv = pos->prev;
	prv->next = newnode;
	newnode->prev = prv;
	newnode->next = pos;
	pos->prev = newnode;

}

3.10在任意位置删除

删除一定注意保护头结点---使用断言

void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* posprev = pos->prev;
	LTNode* posnext = pos->next;
	free(pos);
	posprev->next = posnext;
	posnext->prev = posprev;
}

也是可以复用实现头删尾删

3.11链表的销毁 

链表不是连续的空间就不会像那种顺序表找到头一下子释放而是注意释放

 

void LTDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
	//可以外部置空,不设置返回值和二级指针。

}

 4、结语及整个源码含测试用例

双向链表是一个快捷使用的数据结构,非常实用,不过对于初学建议画图来理解增删查改。很大程度上增删查改避免了二级指针的传参,就是因为使用了哨兵位的好处。以上就是本期的所有内容,知识含量蛮多,大家可以配合解释和原码运行理解。创作不易,大家如果觉得还可以的话,欢迎大家三连,有问题的地方欢迎大家指正,一起交流学习,一起成长,我是Nicn,正在c++方向前行的奋斗者,数据结构内容持续更新中,感谢大家的关注与喜欢。

源码:

test.c

#include"List.h"
void test()
{

	LTNode* plist = LTInit();
	LTPushBack(plist, 1);
	
	LTPushBack(plist, 2);
	
	LTPushBack(plist, 3);
	LTPodFront(plist);
	LTPushFront(plist, 1);
	
	LTPrint(plist);
	printf("\n");
	//LTInsert(plist,1);
	//LTPrint(plist);
}
int main()
{

	test();
	return 0;
}

 List.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int LTDataTYpe;
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataTYpe data;
}LTNode;

//创建新节点函数
LTNode* BuyNode(LTDataTYpe x);
//初始化创建哨兵位
LTNode* LTInit();
//尾插一下
void LTPushBack(LTNode* phead, LTDataTYpe x);
//打印链表
void LTPrint(LTNode* phead);
//尾删
void LTPopBack(LTNode* phead);
//计算链表的大小或者记录链表的大小
int LTSize(LTNode* phead);
//头删函数
void LTPodFront(LTNode* phead);
//头插一下
void LTPushFront(LTNode* phead, LTDataTYpe x);
//查找函数
LTNode* LTFind(LTNode* phead, LTDataTYpe x);
//在寻找到的任意pos位置之前插入
void LTInsert(LTNode* pos, LTDataTYpe x);

//删除任意位置,pos
void LTErase(LTNode* pos);
//链表的销毁
//链表不是连续的空间就不会像那种顺序表找到头一下子释放而是注意释放
void LTDestory(LTNode* phead);

List.c

LTNode* BuyNode(LTDataTYpe x)
{
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	if (phead == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	phead->data = x;
	phead->next = NULL;
	phead->prev = NULL;
	return phead;
}


LTNode* LTInit()
{
	
	LTNode* phead = BuyNode(-1);//首先有一个节点
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

void LTPushBack(LTNode* phead, LTDataTYpe x)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* newnode = BuyNode(x);
	newnode->prev = tail;
	tail->next = newnode;
	newnode->next = phead;
	phead->prev = newnode;
	
}
void LTPushFront(LTNode* phead, LTDataTYpe x)
{
	assert(phead);
	LTNode* d1 = phead->next;
	LTNode* newnode = BuyNode(x);
	phead->next = newnode;
	newnode->next = d1;
	d1->prev = newnode;
	newnode->prev = phead;

}

void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur= phead->next;
	while (cur != phead)
	{
		//printf("phead");
		printf("%d<=>", cur->data);
		cur = cur->next;

	}

}

void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;
	free(tail);
	tail->next = phead;
	phead->prev = tailprev;
}



int LTSize(LTNode* phead)
{
	assert(phead);//不能是空指针
	int size = 0;
	LTNode* cur = phead->next;//定义遍历指针
	while (cur != phead)
	{
		size++;
		cur = cur->next;

	}
	return size;
}

LTNode* LTFind(LTNode* phead, LTDataTYpe x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur!= phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}



void LTInsert(LTNode* pos, LTDataTYpe x)
{
	//首先要查找的节点不能为空
	assert(pos);
	LTNode* newnode = BuyNode(x);
	LTNode* prv = pos->prev;
	prv->next = newnode;
	newnode->prev = prv;
	newnode->next = pos;
	pos->prev = newnode;

}


void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* posprev = pos->prev;
	LTNode* posnext = pos->next;
	free(pos);
	posprev->next = posnext;
	posnext->prev = posprev;
}

void LTPodFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LTNode* after = phead->next;
	LTNode* tail = after->next;
	phead->next = tail;
	tail->prev = phead;
	free(after);
}

void LTDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
	//可以外部置空,不设置返回值和二级指针。

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1488093.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

程序人生:当看到男友学测试拿高薪后,我突然悟了......

Hello&#xff0c;大家好&#xff0c;我是小雨 认识软件测试是因为我男朋友&#xff0c;他在华测教育培训软测后出去就业非常不错&#xff0c;所以我也萌生了转行的想法&#xff0c;当时我的想法是&#xff0c;他都能学会&#xff0c;那我肯定没问题&#xff0c;所以在他的介绍…

【编程系列----编译文件解析】Program size: Code, RO-data , RW-data, ZI-data

1.先给个 axf 文件 转编译bin文件的代码 2.解析下编译文件MAP 先给个编译提示 Code 是代码占用的空间 RO-data是 Read Only 只读常量的大小&#xff0c;如const型 RW-data是&#xff08;Read Write&#xff09; RW是可读可写变量&#xff0c;就是初始化时候就已经赋值了的&a…

图书管理系统的设计与实现

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一 、设计说明 1.1 课题…

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:组件内容模糊)

为当前组件添加内容模糊效果。 说明&#xff1a; 从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 foregroundBlurStyle foregroundBlurStyle(value: BlurStyle, options?: ForegroundBlurStyleOptions) 为当前组件提供…

Linux 学习笔记(12)

十二、 系统服务 1 、系统服务分类&#xff0c;根据其使用的方法来分&#xff0c;可以被分为三类 a、由 init 控制的服务&#xff1a;基本都是系统级别的服务&#xff0c;运行级别这一章讲的就是这一类的服务 b、由 System V 启动脚本启动的服务&#xff1a;和我们打交道最多…

政务浏览器——打通信创闭环最后一公里

当前&#xff0c;信创建设工作主要集中在芯片、操作系统、数据库以及pc整机&#xff0c;这些领域基本可用&#xff0c;或者达到了市场主流水平。但是&#xff0c;政务办事场景下的信创落地仍然困难重重&#xff0c;很多地方不得不装双系统或买两台设备来来平衡日常业务和信创考…

关于企业数字化转型:再认识、再思考、再出发

近年来&#xff0c;随着国家数字化政策不断出台、新兴技术不断进步、企业内生需求持续释放&#xff0c;数字化转型逐步成为企业实现高质量发展的必由之路&#xff0c;成为企业实现可持续发展乃至弯道超车的重要途径。本文重点分析当下阻碍企业数字化转型的难点&#xff0c;提出…

[项目设计] 从零实现的高并发内存池(二)

&#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[高并发内存池] ❤️ 前置学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 ​ 目录 2.高并发内存池整体架构 3.ThreadCache实现 3.1 ThreadCache整体架构…

【JSON2WEB】08 Amis的事件和校验

CRUD操作中&#xff0c;新增、编辑、删除数据后要同步刷新列表&#xff0c;这个可以用Amis的事件来实现。 1 新增数据后刷新列表 Step 1 找到【新增数据】弹窗的【提交】按钮 Step 2 添加鼠标点击事件 这里的 组件ID&#xff1a;u:13d67a44214e 为表格2的组件ID&#xff0c; …

2024常用开源测试开发工具!

今天为大家奉献一篇测试开发工具集锦干货。在本篇文章中&#xff0c;将给大家推荐几款日常工作中经常用到的测试开发工具神器&#xff0c;涵盖了自动化测试、性能压测、流量复制、混沌测试、造数据等。 1、AutoMeter-API 自动化测试平台 AutoMeter 是一款针对分布式服务&…

MySQL 8.0.35 企业版安装和启用TDE插件keyring_encrypted_file

本文主要记录MySQL企业版TDE插件keyring_encrypted_file的安装和使用。 TDE说明 TDE( Transparent Data Encryption,透明数据加密) 指的是无需修改应用就可以实现数据的加解密&#xff0c;在数据写磁盘的时候加密&#xff0c;读的时候自动解密。加密后其他人即使能够访问数据库…

Vue-03

Vue指令 v-bind 作用&#xff1a;动态设置html的标签属性&#xff08;src url title…&#xff09; 语法&#xff1a;v-bind:属性名"表达式" 举例代码如下&#xff1a; 实现效果如下&#xff1a; 案例&#xff1a;图片切换 实现代码如下&#xff1a; 实现的效果…

#WEB前端(CCS常用属性,补充span、div)

1.实验&#xff1a; 复合元素、行内元素、块内元素、行内块元素 2.IDE&#xff1a;VSCODE 3.记录&#xff1a; span为行内元素&#xff1a;不可设置宽高&#xff0c;实际占用控件决定分布空间。 div为块内元素&#xff1a;占满整行&#xff0c;可以设置宽高 img为行内块元…

新手想玩硬件,买单片机还是树莓派好?

新手想玩硬件&#xff0c;买单片机还是树莓派好&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#x…

单链表的排序-力扣算法题

文章目录 概要例题解题思路&#xff1a;1、递归分割2、递归排序实际的含义3、递归回溯与合并 case解析&#xff1a;1、初始链表&#xff1a;2、第一轮分割&#xff1a;3、继续分割&#xff1a;有序子链表合并&#xff1a;最终合并&#xff1a;结果&#xff1a; 代码实现总结&am…

JavaScript继承 寄生组合式继承 extends

JavaScript继承 1、JS 的继承到底有多少种实现方式呢? 2、ES6 的 extends 关键字是用哪种继承方式实现的呢? 继承种类 原型链继承 function Parent1() {this.name parentlthis.play [1, 2, 3] }function Child1() {this.type child2 }Child1.prototype new Parent1(…

(十)SpringCloud系列——openfeign的高级特性实战内容介绍

前言 本节内容主要介绍一下SpringCloud组件中微服务调用组件openfeign的一些高级特性的用法以及一些常用的开发配置&#xff0c;如openfeign的超时控制配置、openfeign的重试机制配置、openfeign集成高级的http客户端、openfeign的请求与响应压缩功能&#xff0c;以及如何开启…

bashplotlib,一个有趣的 Python 数据可视化图形库

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站AI学习网站。 目录 前言 什么是Bashplotlib库&#xff1f; 安装Bashplotlib库 使用Bashplotlib库 Bashplotlib库的功能特性 1. 绘…

Linux速览(1)——基础指令篇

在上一章对Linux有了一些基础了解之后&#xff0c;本章我们来学习一下Linux系统下一些基本操作的常用的基础指令。 目录 1. ls 指令 2. pwd&&whoami命令 3. cd 指令 4. touch指令 5.mkdir指令&#xff08;重要&#xff09;&#xff1a; 6.rmdir指令 && …

pandas行列求众数及按列去重

创建示例数据框如下&#xff1a; df2pd.DataFrame(data{A:[1,2,3,3,4,4,4,4],B:[a,b,c,c,d,d,d,d],C:[11,22,33,33,44,44,44,44],D:[11,22,33,33,44,44,44,44]}) print(df2.mode()) #求列众数 print(df2.loc[:,[A,C,D]].mode(axis1)) #求特定列的行众数 df2.drop_duplicates(s…