追梦之旅【数据结构篇】——详解C语言动态实现带头结点的双向循环链表结构

news2025/7/14 16:44:54

详解C语言动态实现带头结点的双向循环链表结构~😎

  • 前言🙌
  • 预备小知识💞
    • 链表的概念及结构🙌
  • 预备小知识💞
    • 链表的概念及结构🙌
    • 带头结点的双向循环链表结构🙌
  • 整体实现内容分析💞
  • 1.头文件编码实现🙌
  • 2.代码功能实现🙌
    • 1)这是生成新节点函数实现。😊
    • 2)生成带头结点的空链表函数实现。😊
    • 3)删除链表函数实现。😊
    • 4)顺序输出链表函数实现。😊
    • 5)尾插函数实现。😊
    • 6)头插函数实现。😊
    • 7)头删函数实现。😊
    • 8)尾删函数的实现。😊
    • 9)查找函数实现。😊
    • 10)pos位置之前插入x的函数实现。😊
    • 11)删除pos位置的值的函数实现。😊
    • 12)逆序输出的函数实现。😊
    • 13)判空函数实现。😊
  • 3.测试文件源码分享:🙌
  • 总结撒花💞

追梦之旅,你我同行

   
😎博客昵称:博客小梦
😊最喜欢的座右铭:全神贯注的上吧!!!
😊作者简介:一名热爱C/C++,算法等技术、喜爱运动、热爱K歌、敢于追梦的小博主!

😘博主小留言:哈喽!😄各位CSDN的uu们,我是你的博客好友小梦,希望我的文章可以给您带来一定的帮助,话不多说,文章推上!欢迎大家在评论区唠嗑指正,觉得好的话别忘了一键三连哦!😘
在这里插入图片描述

前言🙌

    哈喽各位友友们😊,我今天又学到了很多有趣的知识现在迫不及待的想和大家分享一下!😘我仅已此文,手把手带领大家学习C语言动态实现实现带头结点的双向循环链表结构~ 用代码来实现一个带头结点的双向循环链表结构,完成增删查改,顺序输出和逆序输出,判空的功能。都是精华内容,可不要错过哟!!!😍😍😍

预备小知识💞

链表的概念及结构🙌

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链
接次序实现的 。

预备小知识💞

链表的概念及结构🙌

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链
接次序实现的 。

在这里插入图片描述

带头结点的双向循环链表结构🙌

在这里插入图片描述带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了

整体实现内容分析💞

首先在头文件先定义结构体和各个功能函数的声明,并把有关的头文件包含上去。各个函数如何实现的,已在实验步骤描述清楚,这里就不在赘述了,主要是对各个函数的实现,用到malloc动态开辟新节点的空间,用assert断言确保指针有效,通过画图来帮助理清代码实现的思路,指针的指向如何,要考虑哪些情况。然后再测试代码中,将上述代码都进行测试,显示结果。

1.头文件编码实现🙌

12、	#pragma once
3、	#include <stdio.h>
4、	#include <stdlib.h>
5、	#include <assert.h>
6、	#include <stdbool.h>
78typedef int LTDataType;
9// 带头双向循环链表结构体
10typedef struct ListNode
11{
12struct ListNode* next;
13struct ListNode* prev;
14、		LTDataType data;
15}ListNode;
16、	ListNode* ListInit();//链表初始化
17void ListDestory(ListNode* phead);//删除链表
18void ListPrintFront(ListNode* phead);//链表顺序输出
19void ListPrintBack(ListNode* phead);//链表逆序输出
20void ListPushBack(ListNode* phead, LTDataType x);//尾插
21void ListPushFront(ListNode* phead, LTDataType x);//头插
22void ListPopFront(ListNode* phead);//头删
23void ListPopBack(ListNode* phead);//尾删
24、	ListNode* ListFind(ListNode* phead, LTDataType x);//查找
25void ListInsert(ListNode* pos, LTDataType x);// pos位置之前插入x
26void ListErase(ListNode* pos);// 删除pos位置的值
27、	bool ListEmpty(ListNode* phead);判空函数

2.代码功能实现🙌

1)这是生成新节点函数实现。😊

代码实现思路详解:

//用malloc动态开辟新节点的空间,把数值赋值给新节点
//,newnode的next和prev指针指向空。

ListNode* BuyListNode(LTDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

2)生成带头结点的空链表函数实现。😊

代码实现思路详解:

//2)生成带头结点的空链表,将头结点的指针都指向自己即可。

ListNode* ListInit()
{
	ListNode* phead = BuyListNode(0);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

3)删除链表函数实现。😊

代码实现思路详解:

//3)先用assert断言以下确保phead指针不为空,生成cur指针先指向第一个结点位置。用while循环执行删表:在定义一个next指针指向cur的下一个结点,free掉cur指向的结点,cur移动到next位置。最后删除头结点即可
//


void ListDestory(ListNode* phead)//删除链表
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}

	free(phead);
	phead = NULL;
}

4)顺序输出链表函数实现。😊

代码实现思路详解:

//4)顺序输出链表。先assert断言确保指针有效。定义一个指向链表首结点的指针,打印完一个,cur到下一个节点知道cur回到phead为止。

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

5)尾插函数实现。😊

代码实现思路详解:

//5)尾插函数。先assert确保指针有效性,定义tail指针指向最后一个节点,然后生成新节点然后通过指针将tail指向的节点与新生成的节点相连,新生成的节点与哨兵位头结点相连


void ListPushBack(ListNode* phead, LTDataType x)
{
	assert(phead);

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

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

6)头插函数实现。😊

代码实现思路详解:

//6)头插函数实现。先assert断言一下确保传入进来的指针有效。定义一个指向首节点的指针然后生成一个新节点,让新节点与头结点相连,让新节点的next指针指向原来首节点,原来首节点的prev指向新节点让新节点位于原来首节点的前面从而实现头插。

void ListPushFront(ListNode* phead, LTDataType x)
{
	assert(phead);

	ListNode* first = phead->next;
	ListNode* newnode = BuyListNode(x);
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;
}

7)头删函数实现。😊

代码实现思路详解:

//7)头删函数实现。assert确保传入指针有效和确保头结点不要被删掉。定义first指针指向链表首节点,second指向first下一个。然后让头结点与second指针指向的节点相连,然后free掉first指向的节点。


void ListPopFront(ListNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	ListNode* first = phead->next;
	ListNode* second = first->next;
	phead->next = second;
	second->prev = phead;
	free(first);
    first = NULL;

	
}

8)尾删函数的实现。😊

代码实现思路详解:

//8)尾删函数的实现。头删函数实现。assert确保传入指针有效和确保头结点不要被删掉。定义tail函数指向尾节点,定义prev指针指向tail的前一个节点,让prev指向的那个节点与头结点相连,然后删掉tail指向的尾节点,实现尾删。

void ListPopBack(ListNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	ListNode* tail = phead->prev;
	ListNode* prev = tail->prev;

	prev->next = phead;
	phead->prev = prev;

	free(tail);
	tail = NULL;

}

9)查找函数实现。😊

代码实现思路详解:

//9)查找函数,先assert断言确保传入指针有效性,定义一个cur指向首节点,然后利用while遍历链表,找到的话就返回指针。找不到则返回空。

ListNode* ListFind(ListNode* phead, LTDataType x)
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

10)pos位置之前插入x的函数实现。😊

代码实现思路详解:

// 10)pos位置之前插入x。定义一个prev指针指向pos指向的前一个节点,然后新生成一个节点,通过指针相连方式将新生成的节点放到pos与prev的中间。

void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* prev = pos->prev;
	ListNode* newnode = BuyListNode(x);

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

11)删除pos位置的值的函数实现。😊

代码实现思路详解:

// 11)删除pos位置的值。定义一个prev指针指向pos前一个节点,定义一个next指向pos后一个节点,然后让这两个节点相连,删掉pos指向的节点。

void ListErase(ListNode* pos)
{
	assert(pos);

	ListNode* prev = pos->prev;
	ListNode* next = pos->next;
	prev->next = next;
	next->prev = prev;
	free(pos);
}

12)逆序输出的函数实现。😊

代码实现思路详解:

//12)逆序输出函数实现。定义一个phead指针指向尾节点,从后往前遍历链表,直到cur走到头结点为止。

void ListPrintBack(ListNode* phead)
{
	assert(phead);
	ListNode* cur = phead->prev;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->prev;
	}
	printf("\n");
}

13)判空函数实现。😊

代码实现思路详解:

//13)判空函数实现,只要phead的前指针指向自己,则认为这链表为空链表。

bool ListEmpty(ListNode* phead)
{
	assert(phead);
	if (phead->prev == phead)
	{
		return 1;
	}
	return -1;

3.测试文件源码分享:🙌


#include "SlistNode.h"

void TestList1()
{
	//创建带头节点的空双向链表
	ListNode* plist = ListInit();
	//判空
	int ret = ListEmpty(plist);
	if (ret == 1)
	{
		printf("链表为空\n");
	}
	else
	{
		printf("链表不为空\n");
	}

	//尾插1,2,3,4,5
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 5);
	ListPrintFront(plist);
	//头插0,-1
	ListPushFront(plist, 0);
	ListPushFront(plist, -1);
	ListPrintFront(plist);
	//头删三个数据
	ListPopFront(plist);
	ListPopFront(plist);
	ListPopFront(plist);
	ListPrintFront(plist);
	//尾删一个数据
	ListPopBack(plist);
	ListPrintFront(plist);
	//判空
	int ret1 = ListEmpty(plist);
	if (ret1 == 1)
	{
		printf("链表不为空\n");
	}
	else
	{
		printf("链表为空\n");
	}
	// 查找,附带着修改的作用
	ListNode* pos = ListFind(plist, 3);
	if (pos)
	{
		pos->data *= 10;
		printf("找到了,并且节点的值乘以10\n");
	}
	else
	{
		printf("没有找到\n");
	}
	ListPrintFront(plist);
	//在pos前插入一个300
	ListInsert(pos, 300);
	ListPrintFront(plist);
	//删除pos位置的节点
	ListErase(pos);
	//顺序输出链表数据
	ListPrintFront(plist);
	//逆序输出链表数据
	ListPrintBack(plist);
//删除链表
	ListDestory(plist);

}

int main()
{
	TestList1();

	return 0;
}

程序功能测试结果图:
在这里插入图片描述

总结撒花💞

   本篇文章旨在分享详解C语言动态实现带头结点的双向循环链表结构。希望大家通过阅读此文有所收获实现带头结点的双向循环链表结构,相比于单链表结构更加复杂一点,它的成员相比单链表多了一个指针。本次实现难点在于对指针的运用和对多种情况的考虑。在实现前一定要通过画图的方式将思路和需要注意的情况想清楚然后再来尝试用代码进行实现。基本实现出双链表后可以思考能不能优化代码的问题。
   😘如果我写的有什么不好之处,请在文章下方给出你宝贵的意见😊。如果觉得我写的好的话请点个赞赞和关注哦~😘😘😘

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

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

相关文章

openpose在win下环境配置

1.下载OpenPose库 以下二选一进行下载源码 (1)git进行下载 打开GitHub Desktop或者Powershell git clone https://github.com/CMU-Perceptual-Computing-Lab/openpose cd openpose/ git submodule update --init --recursive --remote(2)在github上手动下载 由于下载环境问…

SpringCloud Alibaba 之Nacos集群部署-高可用保证

文章目录Nacos集群部署Linux部署docker部署&#xff08;参考待验证&#xff09;Nacos 集群的工作原理Nacos 集群中 Leader 节点是如何产生的Nacos 节点间的数据同步过程官方推荐用户把所有服务列表放到一个vip下面&#xff0c;然后挂到一个域名下面。http://nacos.com:port/ope…

go进阶(1) -深入理解goroutine并发运行机制

并发指的是同时进行多个任务的程序&#xff0c;Web处理请求&#xff0c;读写处理操作&#xff0c;I/O操作都可以充分利用并发增长处理速度&#xff0c;随着网络的普及&#xff0c;并发操作逐渐不可或缺 一、goroutine简述 在Golang中一个goroutines就是一个执行单元&#xff…

活动报名丨亚马逊科学家杨靖锋:复现和使用GPT3/ChatGPT的想法

2023年2月23日&#xff08;星期四&#xff09;11:00-12:00&#xff0c;由智源社区主办的「智源LIVE 第30期丨亚马逊科学家杨靖锋讲解『一些关于复现和使用GPT3/ChatGPT的想法』将在线举办&#xff0c;敬请期待。杨靖锋杨靖锋是来自亚马逊的科学家。他的研究主要集中在自然语言处…

ESP32设备驱动-深度休眠与唤醒

深度休眠与唤醒 文章目录 深度休眠与唤醒1、ESP32的休眠模式介绍1.1 ESP32的休眠模式1.2 RTC_GPIO1.3 唤醒源1.4 唤醒流程2、软件准备3、硬件准备4、定时器唤醒5、触摸唤醒6、外部中断唤醒6.1 外部中断ext0唤醒6.2 外部中断ext1唤醒6.3 多GPIO唤醒本文将详细在Arduino IDE中如何…

Spring MVC常用功能及注解

目录 一、什么是Spring MVC 1.1 Spring MVC定义 1.2 MVC定义 1.3 MVC和Spring MVC的关系 1.4 Spring MVC的作用 二、Spring MVC的使用 2.1 Spring MVC的创建和连接 2.1.1 RequestMapping注解 2.1.2 GetMapping注解 2.1.3 PostMapping注解 2.2 获取参数 2.2.1 获取单…

unbound部署DNS

dns的主要作用是将域名解析为ip地址然后在进行访问 安装配置dns准备3台服务器&#xff08;地址都要设置为静态的以便自己配置dns&#xff09;192.168.92.1 用户ip地址&#xff08;windows&#xff09; 192.168.92.132 dns服务器地址 192.168.92.133 web服务器地址 dns服务器 …

网络编程之TCP 网络应用程序开发流程

TCP 网络应用程序开发流程学习目标能够知道TCP客户端程序的开发流程1. TCP 网络应用程序开发流程的介绍TCP 网络应用程序开发分为:TCP 客户端程序开发TCP 服务端程序开发说明:客户端程序是指运行在用户设备上的程序 服务端程序是指运行在服务器设备上的程序&#xff0c;专门为客…

【沐风老师】3DMAX橱柜生成器工具使用教程

3DMAX橱柜生成器工具使用教程 【生成的橱柜模型】 3DMAX橱柜生成器&#xff08;Kitchen Cabinet Creator&#xff09;用于在3ds Max中自动制作橱柜模型。是需要频繁设计厨房、家具和室内设计的用户的理想工具插件&#xff0c;也是所有希望通过点击几下鼠标来填充一些空白空间的…

游戏服务器算法-AOI九宫格python实现

将空间按照一定的方法进行分割&#xff0c;例如根据AOI范围的大小将整个游戏世界切分为固定大小的格子。当游戏物体位于场景的时候&#xff0c;根据坐标将它放入特定的格子中。 例如玩家1在位置7中&#xff0c;如果游戏内的AOI的范围为1个格子。当我们需要获取这个玩家周围的AO…

第九章 - 多表查询(join,left join 等),合并查询(union union all),子查询

第九章 - 多表查询&#xff08;join&#xff0c;left join 等&#xff09;&#xff0c;合并查询&#xff08;union & union all&#xff09;&#xff0c;子查询交叉链接&#xff08;笛卡尔积&#xff09;内连接查询外连接查询左链接&#xff1a; left join右链接&#xff1…

python3遍历目录的三种方法浅谈

日期&#xff1a;2023年2月22日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

JavaScript中怎么实现链表?

JavaScript中怎么实现链表&#xff1f; 学习数据结构的的链表和树时&#xff0c;会遇到节点&#xff08;node&#xff09;这个词&#xff0c;节点是处理数据结构的链表和树的基础。节点是一种数据元素&#xff0c;包括两个部分&#xff1a;一个是实际需要用到的数据&#xff1b…

MATLAB | 如何用MATLAB绘制这样有气泡感的网络图

今天给大家带来一款用来绘制有气泡感的网络图的工具函数&#xff0c;绘制效果如下&#xff1a; 花里胡哨的&#xff0c;气泡大小代表流入流出数据量综合&#xff0c;不同颜色的气泡代表属于不同类&#xff0c;两个气泡之间有连线代表有数据流动&#xff0c;连线透明度代表流动数…

木鱼cms 审计小结

MuYuCMS基于Thinkphp开发的一套轻量级开源内容管理系统,专注为公司企业、个人站长提供快速建站提供解决方案。‍环境搭建我们利用 phpstudy 来搭建环境&#xff0c;选择 Apache2.4.39 MySQL5.7.26 php5.6.9 &#xff0c;同时利用 PhpStorm 来实现对项目的调试‍漏洞复现分析‍…

经过深思熟虑后的接口测试自动化的总结与思考

序近期看到阿里云性能测试 PTS 接口测试开启免费公测&#xff0c;本着以和大家交流如何实现高效的接口测试为出发点&#xff0c;本文包含了我在接口测试领域的一些方法和心得&#xff0c;希望大家一起讨论和分享&#xff0c;内容包括但不仅限于&#xff1a;服务端接口测试介绍接…

中央一号文件首提“即时零售”,县域掀起消费业态新风潮

经过几年的探索&#xff0c;即时零售已经逐步走向成熟&#xff0c;并开始向三四线城市以及乡镇城市渗透。 过去一年&#xff0c;京东、美团、阿里争先布局即时零售市场&#xff0c;完善即时配送网络、培养用户消费习惯&#xff0c;即时零售订单迎来了骤增。2022年下半年&#…

【字节面试】Fail-fast知识点相关知识点

字节面试&#xff0c;问到的一个小知识点&#xff0c;这里做一下总结&#xff0c;其实小编之前有一篇文章&#xff0c;已经对此有过涉及&#xff0c;不过这里知识专项针对于问题&#xff0c;把这个知识点拎出来说一下。 1.问题 什么是Fail-fast机制&#xff1f; Hashmap是否拥…

斯坦福大学团队提出AI生成文本检测器DetectGPT,通过文本对数概率的曲率进行可解释判断

原文链接&#xff1a;https://www.techbeat.net/article-info?id4583 作者&#xff1a;seven_ 随着以ChatGPT等大型语言模型&#xff08;large language models&#xff0c;LLMs&#xff09;的爆火&#xff0c;学界和工业界目前已经开始重视这些模型的安全性&#xff0c;由于C…

DSP28系列 CCS 开发问题总结及解决办法

文章目录 问题汇总 1. CCS编译器的Project菜单栏工程导入选项丢失&#xff0c;怎么解决&#xff01; 1.1启动CCS后发现导入工程菜单栏丢失&#xff0c;无法导入工程文件。 1.2方法一 工程选项的导入工程文件丢失&#xff0c;如果要重新获得相应的选项&#xff0c;就需要删除当前…