【双向链表】

news2024/11/25 4:06:16

双向链表

  • 带头双向循环链表的实现
    • 1. 函数的声明
    • 2. 函数的实现
    • 3. 主函数测试

带头双向循环链表的实现

今天我们来实现一下带头双向循环链表,顾名思义,带头就是有哨兵位,哨兵位不是链表的头,它是连接头节点的一个节点,方便操作链表而已;双向就是一个节点可以找到下一个节点,也可以找到上一个节点,所以每个节点应该有两个结构体指针;循环就是没有空指针,哨兵位的上一个节点是尾,尾的下一个节点是哨兵位;如下图为带头双向循环链表:

在这里插入图片描述

1. 函数的声明

以下是函数的声明部分,我们主要实现双向链表的增删查改功能;

		#pragma once
		#include <stdio.h>
		#include <stdlib.h>
		#include <assert.h>
		#include <stdbool.h>
		
		//类型的命名
		typedef int LTDataType;
		
		//定义节点
		typedef struct ListNode
		{
			struct ListNode* prev;
			struct ListNode* next;
			LTDataType data;
		}LTNode;
		
		//初始化链表
		LTNode* ListInit();
		
		//打印链表
		void ListPrint(LTNode* phead);
		
		//判断是否是空链表
		bool IsEmpty(LTNode* phead);
		
		//尾插、头插、头删、尾删
		void ListPushBack(LTNode* phead, LTDataType x);
		void ListPushFront(LTNode* phead, LTDataType x);
		void ListPopFront(LTNode* phead);
		void ListPopBack(LTNode* phead);
		
		//查找
		LTNode* LTFindPos(LTNode* phead, LTDataType x);
		
		//在pos位置之前插入
		void LTInsertPos(LTNode* pos, LTDataType x);
		
		//删除pos位置
		void LTErasePos(LTNode* pos);
		
		//销毁
		void LTDestroy(LTNode* phead);

2. 函数的实现

为了防止创建新的节点的时候重复出现,我们将创建节点写成一个函数:

		LTNode* BuyListNode(int x)
		{
			LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
			assert(newnode);
		
			newnode->data = x;
			newnode->next = NULL;
			newnode->prev = NULL;
			return newnode;
		}

初始化节点,只需要一个哨兵位,它的next和prev都指向自己;

		LTNode* ListInit()
		{
			LTNode* phead = BuyListNode(-1);
			phead->next = phead;
			phead->prev = phead;
		
			return phead;
		}

打印链表的函数:

		void ListPrint(LTNode* phead)
		{
			assert(phead);
		
			LTNode* cur = phead->next;
			printf("guard<==>");
			while (cur != phead)
			{
				printf("%d<==>",cur->data);
				cur = cur->next;
			}
			printf("\n");
		}

由于头删尾删的时候不能是空链表,带头的链表的空链表相当于只有一个哨兵位,所以头删尾删的时候链表中不能只剩下哨兵位;

		bool IsEmpty(LTNode* phead)
		{
			assert(phead);
			return phead->next == phead;
		}

尾插函数:

		void ListPushBack(LTNode* phead,LTDataType x)
		{
			assert(phead);
			LTNode* newnode = BuyListNode(x);
			LTNode* tail = phead->prev;
		
			newnode->next = phead;
			tail->next = newnode;
		
			newnode->prev = tail;
			phead->prev = newnode;
		}

头插函数:

		void ListPushFront(LTNode* phead, LTDataType x)
		{
			assert(phead);
			LTNode* newnode = BuyListNode(x);
			LTNode* next = phead->next;
		
			newnode->next = next;
			newnode->prev = phead;
			next->prev = newnode;
			phead->next = newnode;
		}

头删函数:

		void ListPopFront(LTNode* phead)
		{
			assert(phead);
			assert(!IsEmpty(phead));
		
			LTNode* del = phead->next;
			LTNode* next = del->next;
		
			phead->next = next;
			next->prev = phead;
			free(del);
		}

尾删函数:

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

查找某个节点的函数,返回找到的节点,否则返回空;

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

在pos位置之前插入的插入函数:

		void LTInsertPos(LTNode* pos, LTDataType x)
		{
			assert(pos);
		
			LTNode* newnode = BuyListNode(x);
		
			LTNode* ptr = pos->prev;
		
			ptr->next = newnode;
			newnode->next = pos;
			pos->prev = newnode;
			newnode->prev = ptr;
		}

删除pos位置的函数:

		void LTErasePos(LTNode* pos)
		{
			assert(pos);
			assert(!IsEmpty(pos));
		
			LTNode* ptr = pos->prev;
			LTNode* next = pos->next;
		
			ptr->next = next;
			next->prev = ptr;
			free(pos);
		}

销毁链表的函数:

		void LTDestroy(LTNode* phead)
		{
			assert(phead);
			LTNode* cur = phead->next;
		
			while (cur != phead)
			{
				LTNode* next = cur->next;
				free(cur);
				cur = next;
			}
			free(phead);
		}

3. 主函数测试

		#include "List.h"
		
		int main()
		{
			LTNode* phead = ListInit();
		
			ListPushBack(phead, 1);
			ListPushBack(phead, 2);
			ListPushBack(phead, 3);
			ListPushBack(phead, 4);
		
			ListPushFront(phead, 10);
		
			ListPopBack(phead);
		
			ListPopFront(phead);
		
			LTNode* pos = LTFindPos(phead, 3);
			LTInsertPos(pos, 20);
			LTErasePos(pos);
		
		
			ListPrint(phead);
			LTDestroy(phead);
			return 0;
		}

以上就是带头双向循环链表的基本实现,感兴趣的伙伴可以自行尝试噢!!!

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

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

相关文章

ChatGPT提示词攻略之迭代提示词

当我们在调试程序时&#xff0c;通常很难一次就把程序正常跑起来。这是普遍现象。但我们会借助一些工具和手段&#xff0c;有步骤有流程地去调整程序&#xff0c;最终让程序按照我们想要的样子正常执行。 对于提示词来说也是一样的。当我们向ChatGPT提问时&#xff0c;一开始它…

从操作系统角度了解内存管理

一.内存管理 1.主要功能 内存管理的主要功能有: 内存空间的分配与回收。由操作系统完成主存储器空间的分配和管理&#xff0c;使程序员摆脱存储分配的麻烦&#xff0c;提高编程效率。地址转换。在多道程序环境下&#xff0c;程序中的逻辑地址与内存中的物理地址不可能一致, …

Effective第三版 中英 | 第二章 创建和销毁对象 | 固定资源首选使用依赖注入

文章目录 Effective第三版前言第二章 创建和销毁对象固定资源首选使用依赖注入 Effective第三版 前言 大家好&#xff0c;这里是 Rocky 编程日记 &#xff0c;喜欢后端架构及中间件源码&#xff0c;目前正在阅读 effective-java 书籍。同时也把自己学习该书时的笔记&#xff0…

接招吧!MySQL 10 连问

文章目录 &#x1f349;1. 索引底层采用什么数据结构&#xff1f;为什么不用hash&#x1f349;2. B树与B树区别&#xff1f;为何用B树&#xff1f;&#x1f349;3. 自增主键理解&#xff1f;&#x1f349;4. 为什么自增主键不连续&#x1f349;5. Innodb为什么推荐用自增ID&…

Jetpack Compose 中的基础组件

Button 默认样式 Button的lambda块中可以传入任意的Composable组件&#xff0c;但一般是放一个Text在里面 Button(onClick { println("确认onClick") }) {Text("默认样式") }按钮的宽高 如果想要宽一点或高一点的Button&#xff0c;可以通过Modifier修…

chatgpt赋能python:Python平面图制作教程

Python平面图制作教程 Python是一种高级编程语言&#xff0c;也是数据科学和机器学习领域中使用最广泛的编程语言之一。在数据可视化中&#xff0c;Python语言具有优秀的表现力和灵活性&#xff0c;可以为用户展示各种数据可视化方案。这篇文章将重点介绍Python如何制作平面图…

八字诀 · 十年之约

* * * 原创&#xff1a;刘教链 * * * 号外&#xff1a;今天在“刘教链Pro”发表了两篇文章&#xff0c;《一视同仁&#xff0c;SEC起诉Coinbase》和《SEC起诉币安的官方新闻稿》&#xff0c;欢迎关注“刘教链Pro”并阅读。 * * * 隔夜比特币奋力反弹&#xff0c;重新回升至27k…

激发数学思维:GPT-4实证研究探索挑战性数学问题

深度学习自然语言处理 原创作者&#xff1a;wkk 考虑到自然语言在许多科学和工程领域表达的数学问题的丰富性&#xff0c;使用大语言模型(LLM)来解决数学问题是一项有趣的研究工作。今天给大家介绍一篇微软研究院联合欧美高校关于如何使用GPT-4解决数学问题的研究论文。 之前的…

KCC@深圳 邀你共享『升压手电DIY』的创新之旅!

嘿&#xff0c;亲爱的KCC深圳的社区成员们&#xff01;你们是否曾经梦想过拥有一款强大的手电&#xff0c;能够在黑暗中照亮一切&#xff1f;现在&#xff0c;我将揭开一个让你们眼前一亮的活动——『升压手电DIY』&#xff01; 在这个充满创意的活动中&#xff0c;我们将带你们…

为什么我们是RISC-V的首选 GPU

谈起 GPU&#xff0c;很容易想到 Imagination。毫无疑问&#xff0c;鉴于 Imagination 在图形处理领域的积累&#xff0c;也成为 RISC-V 的首选 GPU。 凭借 30 多年的 PowerVR GPU&#xff0c;Imagination 非常熟悉图形处理器。在过去的时间里&#xff0c;我们不断创新&#xf…

总结893

学习目标&#xff1a; 月目标&#xff1a;6月&#xff08;线性代数强化9讲&#xff0c;背诵15篇短文&#xff0c;考研核心词过三遍&#xff09; 周目标&#xff1a;线性代数强化3讲&#xff0c;英语背3篇文章并回诵&#xff0c;检测 每日必复习&#xff08;5分钟&#xff09;…

java数组and二维数组

文章目录 java一维数组1.数组定义和定义格式1.1 定义1.2格式 2.数组初始化2.1数组动态初始化2.1.1格式 2.2动态数组初始化取值2.2.1格式 2.3 静态数组初始化取值2.3.1格式 3.数组求长度4.数组遍历 java二维数组1.二维数组动态初始化2.二维数组遍历3.二维数组长度 数组赋值1整数…

聊一聊Netflix 技术栈-数据库

大家好&#xff01;今天我们来聊一聊全球最受欢迎的视频流媒体平台Netflix的技术栈。作为一个庞大的流媒体服务提供商&#xff0c;Netflix需要强大的数据库支持来实现规模化的视频播放。让我们一起来看看Netflix选择了哪些数据库来支撑他们的业务。 在Netflix的技术栈中&#x…

define与const什么使用区别

&#x1f6a9;define &#x1f38c;一.define的使用方法 &#x1f380;1.定义常量 #define M 10010 int main() {cout << M << endl;cout << M M << endl;//cout << M << end;//会报错return 0; }我们定义了一个名为M的常数&#xff0…

Effective第三版 中英 | 第二章 创建和销毁对象 | 避免创建不需要的对象

文章目录 Effective第三版前言第二章 创建和销毁对象避免创建不需要的对象 Effective第三版 前言 大家好&#xff0c;这里是 Rocky 编程日记 &#xff0c;喜欢后端架构及中间件源码&#xff0c;目前正在阅读 effective-java 书籍。   同时也把自己学习该书时的笔记&#xff…

我爸嘲讽我,写破代码一年才挣十几万,他在工地带50个工人,一个月仅人头费就挣3万多,让我滚回去跟他干工地!...

现在码农的地位有多低&#xff1f; 一位程序员讲述自己被父亲鄙视的经过&#xff1a; 我爸嘲讽我&#xff0c;说我天天写这破代码有啥用&#xff0c;一年就拿十多万死工资。他在工地带 50 个工人&#xff0c;一个人一天抽 20 块钱人头费&#xff0c;一个月都能抽 3 万多&#x…

MTK 相机内存知识点

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点 一、Camera 内存包含哪些内容二、VSS/RSS/PSS/USS 内存介绍三、内存查看常用命令四、内存泄漏相关知识点五、参考文献 一、Camera 内存包含哪些内容 1.1…

DeepFaceLive AI实时换脸软件安装流程

第一&#xff1a;设置虚拟内存≥32G 在安装DeepFaceLive前&#xff0c;需把电脑的虚拟内存设置≥32G。鼠标移到左下角WIN处&#xff0c;右击后选择【系统】模块。 在显示的【系统信息】页面选择【高级系统设置】。 在显示的【系统属性】页面中&#xff0c;选择【高级】选项&…

【MySQL数据库 | 第四篇】SQL通用语法及分类

目录 &#x1f914;SQL通用语法&#xff1a; &#x1f60a;语句&#xff1a; &#x1f60a;注释&#xff1a; &#x1f914;SQL语句分类&#xff1a; &#x1f60a;1.DDL语句&#xff1a; &#x1f60a;2.DML语句&#xff1a; &#x1f60a;3.DQL语言&#xff1a; &…

不写代码也能年薪百万?Prompt+低代码开发实战

&#x1f449;腾小云导读 近期 AIGC 狂潮席卷&#xff0c;“前端走向穷途”“低代码时代终结”的言论甚嚣尘上。事实上 GPT 不仅不会干掉低代码&#xff0c;反而会大幅度促进低代码相关系统的开发。本文会介绍 GPT Prompt Engineering 的基本原理&#xff0c;以及如何帮助低代码…