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

这篇具有很好参考价值的文章主要介绍了追梦之旅【数据结构篇】——详解C语言动态实现带头结点的双向循环链表结构。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


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

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

😘博主小留言:哈喽!😄各位CSDN的uu们,我是你的博客好友小梦,希望我的文章可以给您带来一定的帮助,话不多说,文章推上!欢迎大家在评论区唠嗑指正,觉得好的话别忘了一键三连哦!😘
追梦之旅【数据结构篇】——详解C语言动态实现带头结点的双向循环链表结构,追梦之旅【数据结构篇】,数据结构,链表,c语言

前言🙌

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

预备小知识💞

链表的概念及结构🙌

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

预备小知识💞

链表的概念及结构🙌

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

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

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

追梦之旅【数据结构篇】——详解C语言动态实现带头结点的双向循环链表结构,追梦之旅【数据结构篇】,数据结构,链表,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语言动态实现带头结点的双向循环链表结构,追梦之旅【数据结构篇】,数据结构,链表,c语言

总结撒花💞

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

到了这里,关于追梦之旅【数据结构篇】——详解C语言动态实现带头结点的双向循环链表结构的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言数据结构一:动态数组

    先说一个概念:数组是一段 连续 的内存空间。存储相同的数据类型。 数组的两个关键点: 连续内存; 相同类型。 首先连续内存:所以为了找到动态数组我们必须找到一个 首元素 地址。(内存 首地址 。) 如果不知道首地址,那无法找到并操作内存空间。 知道首地址了,

    2024年02月06日
    浏览(35)
  • 【数据结构专栏】动态扩容顺序栈详解

      💌 博客内容:顺序栈的原理详解 😀 作  者:陈大大陈 🚀 个人简介:一个正在努力学技术的准前段,专注基础和实战分享 ,欢迎私信! 💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 😘 😘 目录 顺序栈的定义 结构体定义

    2023年04月09日
    浏览(26)
  • 数据结构与算法:动态规划(Dynamic Programming)详解

    动态规划(Dynamic Programming,简称DP) 是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划经常被用于求解优化问题。 动态规划的核心思想是将复杂问题分解为更小的子问

    2024年04月25日
    浏览(39)
  • 【数据结构】顺序表的动态分配(步骤代码详解)

    🎈个人主页:豌豆射手^ 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:数据结构 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 在计算机科学中,数据结构是组织和存储数据的方式,它决定了数据如何被存储、检索和操作。

    2024年04月12日
    浏览(55)
  • 【数据结构】C语言结构体详解

    目录 前言 一、结构体的定义 二、定义结构体变量 三、结构体变量的初始化 四、使用typedef声明新数据类型名 五、指向结构体变量的指针 总结 🌈嗨!我是Filotimo__🌈。很高兴与大家相识,希望我的博客能对你有所帮助。 💡本文由Filotimo__✍️原创,首发于CSDN📚。 📣如需转

    2024年02月04日
    浏览(40)
  • [C语言][数据结构][动态内存空间的开辟]顺序表的实现!

    目录 零.必备知识 a.顺序表的底层是数组. b.数组在内存中是连续存放的. c.动态内存空间的开辟(malloc,calloc,realloc). 一.顺序表的定义与实现          1.1 顺序表的定义          1.2 顺序表的初始化          1.3 顺序表的销毁          1.4 顺序表容量的检查与调整

    2024年04月09日
    浏览(71)
  • 【数据结构】C语言队列(详解)

    前言: 💥🎈个人主页:​​​​​​Dream_Chaser~ 🎈💥 ✨✨专栏:http://t.csdn.cn/oXkBa ⛳⛳本篇内容:c语言数据结构--C语言实现队列 目录 一.队列概念及结构 1.1队列的概念 1.2队列的结构 二.队列的实现 2.1头文件 2.2链式队列的结构定义 2.3队列接口的定义 2.4初始化队列 2.5判断队列

    2024年02月10日
    浏览(31)
  • 数据结构(C语言)——顺序表详解

    数据结构是计算机存储和组织数据的方式。常用的数据结构有:数组(Array)、栈(Stack)、队列(Queue)、链表(Linked List)、树(Tree)、图(Graph)、堆(Heap)等;而数据结构又可以通过逻辑结构与物理结构进行分类,逻辑结构是指数据元素之间的逻辑关系,也就是数据元

    2024年04月16日
    浏览(29)
  • 【数据结构】队列---C语言版(详解!!!)

    队列 :只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有 先进先出FIFO (First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头 入队列 :进行插入操作的一端称为队尾 出队列 :进行删除操作的一端称

    2024年02月10日
    浏览(38)
  • 【数据结构】栈---C语言版(详解!!!)

    栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端称为栈顶,另一端称为栈底 。栈中的数据元素遵守 后进先出LIFO (Last In First Out)的原则。 压栈 :栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈 :栈的删除

    2024年02月10日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包