数据结构【链表】看完还怕拿不下链表?

在这里插入图片描述

✨Blog:🥰不会敲代码的小张:)🥰
🉑推荐专栏:C语言🤪、Cpp😶‍🌫️、数据结构初阶💀
💽座右铭:“記住,每一天都是一個新的開始😁😁😁

前言

上一章节:无头单向非循环链表

具体链表介绍内容请见上一章节
本章节主要介绍双向链表以及带头节点和循环的概念和实现,并且本章是在上一章节的基础上加以改造,虽然结构复杂了点,但是要比单链表更好实现🤪

目录

  • 前言
  • 双链表介绍
  • 链表的创建
  • 创建新节点
  • 初始化头节点
  • 销毁链表结点
  • 打印链表
  • 判断链表是否为空
  • 尾插
  • 尾删
  • 头插
  • 头删
  • 在pos的前一个位置插入
  • 删除pos位置节点
  • 查找

双链表介绍

在这里插入图片描述
从上面的图中我们可以看到,每一个节点都会有一个指针指向前一个和后一个,然后头节点指向最后一个节点,最后一个节点指向头节点,这就是带头双向循环链表。
那为什么会有单链表和双链表之分呢?
在单链表删除或者查询一个元素时,我们只能单向读取,然后如果删除的话,我们要保存前一个节点,为克服单链表的单向性,可以使用双链表更好的解决此问题。
头结点是为了操作的统一与方便而设立的,放在第一个元素结点之前,其数据域一般无意义(当然有些情况下也可存放链表的长度、用做监视哨等等)

链表的创建

prev指针指向前一个,next指针指向后一个

typedef int LDataType;
typedef struct ListNode
{
	LDataType data;
	struct ListNode* prev;
	struct ListNode* next;

}ListNode;

创建新节点

//创建新的节点
ListNode* BuyListNode(LDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("BuyListNode");
		return NULL;
	}
	newnode->next = NULL;
	newnode->prev = NULL;
	newnode->data = x;

	return newnode;
}

初始化头节点

最开始的头节点前后指针都指向自己,如果有元素插入进来再更改指针指向的位置
在这里插入图片描述

//初始化头节点
ListNode* LsitInit()
{
	ListNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

销毁链表结点

把头节点的后一个的节点给cur指针,也就是第一个节点的位置
cur指针指向头节点的时候,以及遍历完了链表,所以循环不再进去
然后保留cur的下一个节点,free掉cur,再把next给cur,继续向后走
在这里插入图片描述

//销毁节点
void ListDestroy(ListNode* phead)
{
	assert(phead);//断言phead不能为空
	ListNode* cur = phead->next;//头节点的下一个才是第一个元素
	while (cur != phead)//cur的next不能等于头节点
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

打印链表

//打印节点数据
void ListPrint(ListNode* phead)
{
	assert(phead);
	ListNode* cur = phead->next;

	printf("head<=>");

	while (cur != phead)
	{
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

判断链表是否为空

如果头节点的next指针指向自己,说明链表为空

bool ListEmpty(ListNode* phead)
{
	assert(phead);
	return phead->next == phead;
}

尾插

只需要更改指针关系,找到最后一个节点的位置,在尾部链接要插入的节点,把新插入的节点前指针和后指针链接到对应的位置即可
在这里插入图片描述

//尾插
void ListPushback(ListNode* phead, LDataType x)
{
	assert(phead);

	ListNode* newnode = BuyListNode(x);
	ListNode* tail = phead->prev;

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

尾删

找到最后一个节点tail,保留tail的前一个tailprev
把tailprev的next指向头节点,头节点的prev指向tailprev
在这里插入图片描述

//尾删
void ListPopback(ListNode* phead)
{
	assert(phead);
	assert(!ListEmpty(phead));

	ListNode* tail = phead->prev;
	ListNode* tailprev = tail->prev;
	
	tailprev->next = phead;
	phead->prev = tailprev;
	free(tail);
	tail = NULL;
}

头插

更改指针关系的时候一定要注意先后顺序,不然一旦指针更改可能就会链接到错误的地方
在这里插入图片描述

void ListPushFront(ListNode* phead, LDataType x)
{
	assert(phead);
	ListNode* newnode = BuyListNode(x);
	
	newnode->next = phead->next;
	phead->next->prev = newnode;
	newnode->prev = phead;
	phead->next = newnode;

	//ListInsert(phead->next, x);
}

头删

注意:只要是删除链表节点,就要判断链表是否为空,如果为空就直接报错
tail指针指向第一个节点,让头节点指针指向tail的next
tail的next的prev指向头节点
最后free掉tail指向的节点
在这里插入图片描述

//头删
void ListPopFront(ListNode* phead)
{
	assert(phead);
	assert(!ListEmpty(phead));

	ListNode* tail = phead->next;
	phead->next = tail->next;
	tail->next->prev = phead;
	free(tail);
	tail = NULL;

	//ListErase(phead->next);

}

在pos的前一个位置插入

注:pos要配合着查找函数一起使用,比如查找6,找到之后返回6的地址给指针。
记录pos前一个的节点,然后再更改指针之间的关系
在这里插入图片描述

//在pos的前一个位置插入
void ListInsert(ListNode* pos, LDataType x)
{
	assert(pos);
	ListNode* newnode = BuyListNode(x);
	ListNode* prev = pos->prev;

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

删除pos位置节点

在这里插入图片描述

//删除pos位置节点
void ListErase(ListNode* pos)
{
	assert(pos);
	assert(!ListEmpty(pos));

	ListNode* prev = pos->prev;

	prev->next = pos->next;
	pos->next->prev = prev;
	free(pos);
	pos = NULL;
}

查找

定义一个cur指针指向第一个节点,如果cur的data和要查找的值一样就返回cur,如果不想等就让cur向后走,直到cur走到头节点循环结束。

//查找
ListNode* ListFind(ListNode* phead, LDataType x)
{
	assert(phead);

	ListNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data != x)
			cur = cur->next;
		else
			return cur;
	}
	return NULL;
}

创作不易,记得三连!笔者水平有限,如有错误的地方,还望指出,谢谢大家😋😋😋

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

“饶派杯”XCTF车联网安全挑战赛战队巡礼!

2023年5月31日&#xff0c;“饶派杯” XCTF车联网安全挑战赛将于江西省上饶市重磅开赛。本届大赛由江西省委网信办、江西省工信厅、上饶市人民政府主办&#xff0c;旨在深入贯彻落实国家网络强国和交通强国战略部署&#xff0c;推动智能网联汽车技术与产业发展、加快该领域人才…

React项目搭建

一、项目搭建&#xff08;不采用vite方式&#xff09; 使用create-react-app生成项目 npx create-react-app pc 进入根目录 cd pc 启动项目 npm start 调整项目目录结构 /src/assets 项目资源文件&#xff0c;比如&#xff0c;图片 等/components 通用组件/pag…

详细分析置换算法

对于操作系统而言&#xff0c;虚拟空间是非常大的&#xff0c;我们往往无法直接将如此大的空间装入内存&#xff0c;而即使我们采用多级页表与段页式存储即使&#xff0c;也仅仅只是节省了页表的大小&#xff0c;如此将如何多的物理页装进内存仍然是一个问题&#xff0c;为此科…

【MySQL学习】MySQL表的复合查询

文章目录 前言一、案例准备二、基本查询三、多表查询四、子查询4.1 单行子查询4.2 多行子查询4.3 多列子查询4.4 FROM子句中的子查询4.5 合并查询4.5.1 UNION4.5.2 UNION ALL 五、自连接六、内外连接6.1 内连接6.2 外连接6.2.1 左外连接6.2.2 右外连接 前言 对MySQL表的基本查…

【容器化应用程序设计和开发】2.7 云原生开发工具和框架

2.7 云原生开发工具和框架 今天我们就简单来讲一下云原生下用到的开发工具和一些基本的框架。云原生开发工具和框架是为了支持现代化的应用程序开发&#xff0c;能够简化云原生应用程序的构建、部署、管理和维护。下面是一些常见的云原生开发工具和框架&#xff1a; Kubernetes…

为什么别人家的ChatGPT比我家的更聪明?

文章目录 引子使用技巧技巧1&#xff1a;使用分隔符技巧2&#xff1a;结构化输出技巧3&#xff1a;整理操作步骤技巧4&#xff1a;做示范技巧5&#xff1a;给定具体的步骤技巧6&#xff1a;生成摘要技巧7&#xff1a;情感分析 好问题的三要素总结 引子 你有没有发现&#xff0…

python+Django音乐播放器网站系统0tr3w

音乐网站系统的后台开发目标是以信息管理系统的管理和开发方法&#xff0c;用目前现有的新技术进行系统开发&#xff0c;提供后台管理员高度友好的界面操作以及迅捷的信息处理。而前台的开发目标是以用户的需求作为主导&#xff0c;提供对用户而言非常友好的界面操作环境以及完…

2023年第十五届B题电工杯初步解题思路

第十五届“中国电机工程学会杯”全国大学生 电工数学建模竞赛题目 B题 人工智能对大学生学习影响的评价 人工智能简称AI&#xff0c;最初由麦卡锡、明斯基等科学家于1956年在美国达特茅斯学院开会研讨时提出。 2016年&#xff0c;人工智能AlphaGo 4:1战胜韩国围棋高手李世石…

(学习日记)AD学习 #2

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

航空公司预订票数学建模论文

航空公司预订票数学建模论文篇1 试谈机票订票模型与求解 一、概述 1. 问题背景描述 在激烈的市场竞争中&#xff0c;航空公司为争取更多的客源而开展的一个优质服务项目是预订票业务,本模型针对预订票业务&#xff0c;建立二元规划订票方案&#xff0c;既考虑航空公司的利润最大…

利用qsort排序

一、简单排序10个元素的一维数组 #define _CRT_SECURE_NO_WARNINGS #pragma warning(disable:6031) #include<stdio.h> #include<stdlib.h> void print_arr(int arr[], int sz) {int i 0;for (i 0; i < sz; i){printf("%d ", arr[i]);}printf("…

开源赋能 普惠未来|QUICKPOOL诚邀您参与2023开放原子全球开源峰会

QUICKPOOL算力调度系统的诞生和发展&#xff0c;为广大的算力领域从业者和技术开发者&#xff0c;提供了一条中国技术路线&#xff0c;并与IBM LSF、SLURM、PBS、SGE等产品&#xff0c;共同助力全球算力发展。QUICKPOOL算力调度系统成熟、稳定&#xff0c;具备“超算&智算”…

服务高可用保障:服务限流,Nginx实现服务限流

一、前言 1.1什么是限流&#xff1f; 限流存在于高可用服务中。 用于高可用的保护手段&#xff0c;主要包括&#xff1a;缓存&#xff0c;降级&#xff0c;限流 限流&#xff1a;只允许指定的事件进入系统&#xff0c;超过的部分将被拒绝服务&#xff0c;排队或者降级处理。 …

国内行业垂直型SaaS公司有哪些?发展前景如何?

01 国内行业垂直型SaaS公司有哪些&#xff1f; 根据艾瑞咨询测算&#xff0c;2021年中国企业级应用软件市场规模达到2592亿元&#xff0c;SaaS在其中占比达到28.1%。 在企业数字化转型的全景图中&#xff0c;SaaS扮演着应用场景层面的关键作用&#xff0c;往往是企业特定环节数…

ChatGPT系列学习(1)transformer基本原理讲解

文章目录 1. 简介1.1. 发展史 2. Transformer 整体结构3. 名词解释3.1. token 4. transformer输入4.1. 单词 Embedding4.2. 位置Embedding4.3. Transformer Embedding层实现 5. Attention结构5.1. 简介5.2. Self Attention&#xff08;自注意力机制&#xff09;5.2.1. 简介5.2.…

mysql 库的操作

文章目录 mysql 库的操作1. 创建数据库创建数据库案例 2. 字符集和校验规则查看系统默认的字符集合校验规则查看数据库支持的字符集查看数据库支持的字符集较验规则校验规则对数据库的影响 3. 操作数据库查看数据库显示创建语句修改数据库删除数据库查看数据库连接情况 mysql 库…

uniapp内使用 mescroll

前言 在使用uniapp开发项目的过程中&#xff0c;在很多场景里都需要下拉刷新和上拉加载&#xff0c;而 mescroll.js 则是一个非常精致的下拉刷新和上拉加载 js 框架。 官网地址&#xff1a;mescroll 介绍 mescroll.js 是在 H5端 运行的下拉刷新和上拉加载插件&#xff0c;时…

Leetcode 1679. K 和数对的最大数目 双指针法

https://leetcode.cn/problems/max-number-of-k-sum-pairs/ 给你一个整数数组 nums 和一个整数 k 。 每一步操作中&#xff0c;你需要从数组中选出和为 k 的两个整数&#xff0c;并将它们移出数组。 返回你可以对数组执行的最大操作数。 示例 1&#xff1a; 输入&#xff1…

【云计算与虚拟化】第五章—— vCenter Server 5.5 的高级功能(三)

第五章—— vCenter Server 5.5 的高级功能&#xff08;三&#xff09; 1.使用vsphere client 登陆vcenter服务器,创建一个群集&#xff0c;名称为自己的学号&#xff0c;&#xff08;截图&#xff09; 2.针对该群集打开HA功能&#xff08;截图&#xff09; 3.接入控制策略选择…

使用Python复制某文件夹下子文件夹名为数据文件夹下的所有以DD开头的文件夹到桌面...

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 楼阁玲珑五云起&#xff0c;其中绰约多仙子。 大家好&#xff0c;我是皮皮。 一、前言 前几天在Python最强王者群【魏哥】问了一个Python自动化办公处理…