带头 双向 循环 链表——C语言实现

news2024/11/20 2:39:00

        既然有带头 那么就有不带头 为什么我要将带头 而不讲不带头?

        在之前我讲单链表时就说过 如果不带头会出现讨论的情况 为什么会出现讨论的情况

        假设链表有一个节点 进行尾插 就只是将新的节点连接到链表的尾节点之后 

        那么如果链表没有节点 就没有尾节点 自然也就不能尾插 也就是说这个新节点要作为链表的头节点 这个操作自然就和上面的尾插不同了 所以要进行讨论

那么接下来就开始 带头双向循环链表的讲解

既然是链表就离不开结构体 就先定义一个结构体出来 

这是双向循环链表的图形

 如上图所示 该结构体中有两个指针 一个指向下一个节点 另一个指向前一个节点 循环也就是尾节点的next 指向头节点 将prior改为prev 头结点的prev指向尾节点

如此就形成了一个环形结构 

typedef struct ListNode
{
	int val;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;

接下来就对节点进行初始化

void ListInti(ListNode* frist)
{
	frist->val = 0;
	frist->next = frist;
	frist->prev = frist;
}

 写一个开辟节点的函数

ListNode* BuyNode(int x)
{
	ListNode* tmp = (ListNode*)malloc(sizeof(ListNode));
	if (tmp == NULL)
	{
		printf("节点开辟失败.");
		return;
	}
	tmp->val = x;
	return tmp;
}

因为下面尾插和头插 都要用到开辟节点 所以都要用 那不如写一个函数进行赋用

void ListPushBack(ListNode* frist, int x)
{
	ListNode* node = BuyNode(x);
	ListNode* tmp = frist->prev;

	tmp->next = node;
	node->prev = tmp;

	node->next = frist;
	frist->prev = node;
}

void ListPushFront(ListNode* frist, int x)
{
	ListNode* node = BuyNode(x);
	ListNode* tmp = frist->next;

	node->prev = frist;
	tmp->prev = node;
	frist->next = node;
	node->next = tmp;
}

计算链表的大小

static int ListSize(ListNode* frist)
{
	ListNode* cur = frist->next;
	int sz = 1;
	while (frist != cur)
	{
		sz++;
		cur = cur->next;
	}
	return sz;
}

下面就是任意位置插入的函数

void ListNodeInsert(ListNode* frist, int pos, int x)
{
	assert(frist);

	if (pos > ListSize(frist))
	{
		printf("插入位置,超过链表长度.\n");
		return;
	}

	ListNode* node = BuyNode(x);
	ListNode* cur = frist, * str = cur->next;
	while (pos > 1)
	{
		pos--;
		cur = cur->next;
		str = str->next;
	}
	node->next = str;
	node->prev = cur;
	cur->next = node;
	str->prev = node;
}

既然可以任意位置插入 那么头插和尾插 都可以赋用这个函数

void ListPushBack(ListNode* frist, int x)
{
	//ListNode* node = BuyNode(x);
	//ListNode* tmp = frist->prev;

	//tmp->next = node;
	//node->prev = tmp;

	//node->next = frist;
	//frist->prev = node;
	ListNodeInsert(frist, ListSize(frist), x);
}

void ListPushFront(ListNode* frist, int x)
{
	//ListNode* node = BuyNode(x);
	//ListNode* tmp = frist->next;

	//node->prev = frist;
	//tmp->prev = node;
	//frist->next = node;
	//node->next = tmp;
	ListNodeInsert(frist, 1, x);
}

节点的增添已经写完了 接下来就是删除的操作

因为节点在堆上开辟  所以要写一个释放节点的函数

void ListNodeDestroy(ListNode* tmp)
{
	free(tmp);
	tmp->next = NULL;
	tmp->prev = NULL;
}

如果链表是空的 那么就没有必要删除

static bool Empty(ListNode* frist)
{
	return frist->next == frist;
}

那么开始就写删除节点的函数 尾删和头删

void ListPopBack(ListNode* frist)
{
	assert(frist);
	assert(!Empty(frist));

	ListNode* tmp = frist->prev, * tail = tmp->prev;
	tail->next = frist;
	frist->prev = tail;

	ListNodeDestroy(tmp);
}

void ListPopFront(ListNode* frist)
{
	assert(frist);
	assert(!Empty(frist));

	ListNode* tmp = frist->next, * tail = tmp->next;
	frist->next = tail;
	tail->prev = frist;

	ListNodeDestroy(tmp);
}

既然上面插入有任意位置 那么就有任意位置的删除操作

void ListNodeErase(ListNode* frist,int pos)
{
	assert(frist);

	ListNode* cur = frist->next, * str = cur->next;
	while (pos > 1)
	{
		pos--;
		cur = cur->next;
		str = str->next;
	}
	str->prev = cur->prev;
	cur->prev->next = str;

	ListNodeDestroy(cur);
}

双向链表的增添 删除 已经完成

链表的打印

static void Print(ListNode* frist)
{
	assert(frist);
	ListNode* cur = frist->next;
	while (frist != cur)
	{
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}

上面就是双向带头循环链表

ListNode* ListFind(ListNode* frist,int x)
{
	assert(frist);
	assert(!Empty(frist));

	ListNode* cur = frist->next;
	int pos = 0;
	while (frist != cur)
	{
		pos++;
		if (cur->val == x)
		{
			printf("存在该数据 在第%d个位置\n",pos);
		}
		cur = cur->next;
	}
}

完整的双向循环链表链表结构

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


typedef struct ListNode
{
	int val;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;


void ListInti(ListNode* frist)
{
	frist->val = 0;
	frist->next = frist;
	frist->prev = frist;
}

ListNode* BuyNode(int x)
{
	ListNode* tmp = (ListNode*)malloc(sizeof(ListNode));
	if (tmp == NULL)
	{
		printf("节点开辟失败.");
		return;
	}
	tmp->val = x;
	return tmp;
}

static void Print(ListNode* frist)
{
	assert(frist);
	ListNode* cur = frist->next;
	while (frist != cur)
	{
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}

static bool Empty(ListNode* frist)
{
	return frist->next == frist;
}

void ListNodeDestroy(ListNode* tmp)
{
	free(tmp);
	tmp->next = NULL;
	tmp->prev = NULL;
}

void ListPopBack(ListNode* frist)
{
	assert(frist);
	assert(!Empty(frist));

	ListNode* tmp = frist->prev, * tail = tmp->prev;
	tail->next = frist;
	frist->prev = tail;

	ListNodeDestroy(tmp);
}

void ListPopFront(ListNode* frist)
{
	assert(frist);
	assert(!Empty(frist));

	ListNode* tmp = frist->next, * tail = tmp->next;
	frist->next = tail;
	tail->prev = frist;

	ListNodeDestroy(tmp);
}

void ListDestroy(ListNode* frist)
{
	assert(frist);
	assert(!Empty(frist));

	ListNode* tmp = frist->next, * cur = tmp->next;
	while (frist != tmp)
	{
		ListNodeDestroy(tmp);
		tmp = cur;
		cur = cur->next;
	}
}

static int ListSize(ListNode* frist)
{
	ListNode* cur = frist->next;
	int sz = 1;
	while (frist != cur)
	{
		sz++;
		cur = cur->next;
	}
	return sz;
}

void ListNodeInsert(ListNode* frist, int pos, int x)
{
	assert(frist);

	if (pos > ListSize(frist))
	{
		printf("插入位置,超过链表长度.\n");
		return;
	}

	ListNode* node = BuyNode(x);
	ListNode* cur = frist, * str = cur->next;
	while (pos > 1)
	{
		pos--;
		cur = cur->next;
		str = str->next;
	}
	node->next = str;
	node->prev = cur;
	cur->next = node;
	str->prev = node;
}

void ListNodeErase(ListNode* frist,int pos)
{
	assert(frist);

	ListNode* cur = frist->next, * str = cur->next;
	while (pos > 1)
	{
		pos--;
		cur = cur->next;
		str = str->next;
	}
	str->prev = cur->prev;
	cur->prev->next = str;

	ListNodeDestroy(cur);
}

//void ListPushBack(ListNode* frist, int x)
//{
//	ListNode* node = BuyNode(x);
//	ListNode* tmp = frist->prev;
//
//	tmp->next = node;
//	node->prev = tmp;
//
//	node->next = frist;
//	frist->prev = node;
//}
//
//void ListPushFront(ListNode* frist, int x)
//{
//	ListNode* node = BuyNode(x);
//	ListNode* tmp = frist->next;
//
//	node->prev = frist;
//	tmp->prev = node;
//	frist->next = node;
//	node->next = tmp;
//}

void ListPushBack(ListNode* frist, int x)
{
	//ListNode* node = BuyNode(x);
	//ListNode* tmp = frist->prev;

	//tmp->next = node;
	//node->prev = tmp;

	//node->next = frist;
	//frist->prev = node;
	ListNodeInsert(frist, ListSize(frist), x);
}

void ListPushFront(ListNode* frist, int x)
{
	//ListNode* node = BuyNode(x);
	//ListNode* tmp = frist->next;

	//node->prev = frist;
	//tmp->prev = node;
	//frist->next = node;
	//node->next = tmp;
	ListNodeInsert(frist, 1, x);
}

ListNode* ListFind(ListNode* frist,int x)
{
	assert(frist);
	assert(!Empty(frist));

	ListNode* cur = frist->next;
	int pos = 0;
	while (frist != cur)
	{
		pos++;
		if (cur->val == x)
		{
			printf("存在该数据 在第%d个位置\n",pos);
		}
		cur = cur->next;
	}
}

void test()
{
	ListNode frist;
	ListInti(&frist);

	ListPushBack(&frist, 1);
	ListPushBack(&frist, 2);
	ListPushBack(&frist, 3);
	ListPushBack(&frist, 4);

	ListPushFront(&frist, 9);
	ListPushFront(&frist, 8);
	ListPushFront(&frist, 7);
	ListPushFront(&frist, 6);

	ListPopBack(&frist);
	ListPopBack(&frist);

	ListPopFront(&frist);
	ListPopFront(&frist);

	ListNodeInsert(&frist, 1, 10);
	ListNodeInsert(&frist, 4, 11);
	ListNodeInsert(&frist, 5, 12);
	ListNodeInsert(&frist, 8, 13);

	ListNodeErase(&frist, 2);
	ListNodeErase(&frist, 7);

	ListFind(&frist, 0);
	ListFind(&frist, 2);

	Print(&frist);

	ListDestroy(&frist);

}

int main()
{

	test();

	return 0;
}

上续双向循环链表已完成链表~

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

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

相关文章

网络编程 (一)网络协议TCP,UDP

文章目录 &#x1f412;个人主页&#x1f3c5;JavaSE系列专栏&#x1f4d6;前言&#xff1a;&#x1f380;计算机网络概述&#x1f380;网络编程&#x1f380;那么是如何精确找到计算机网络中的目标主机呢&#xff1f;&#x1f380;网络模型&#x1f380;TCP协议&#x1f380;U…

GitLAB CI-CD语法

GitLAB CI-CD语法 目录 1、Pipeline核心语法 gitlab-ci语法&#xff1a; https://docs.gitlab.com/ee/ci/yaml/ stages 阶段控制 .pre阶段的作业总是在流水线开始时执行&#xff1b;.post阶段的作业总是在流水线结束时执行&#xff1b; CI代码&#xff1a; stages:- build…

Servlet的详解

Servlet 的主要工作 允许程序员注册一个类&#xff0c;在 Tomcat 收到的某个特定的 HTTP 请求的时候&#xff0c;执行这个类中的一些代码 帮助程序员解析 HTTP 请求&#xff0c;把 HTTP 请求从一个字符串解析成一个 HttpRequest 对象 帮助程序员构造 HTTP 响应&#xff0c;程序…

用 CSS 自定义滚动条

简介 首先需要介绍一下滚动条的组成部分。滚动条包含 track 和 thumb&#xff0c;如下图所示&#xff1a; track是滚动条的基础&#xff0c;其中的 thumb是用户拖动支页面或章节内的滚动。 案例&#xff1a; 案例代码&#xff1a; <!DOCTYPE html> <html><he…

python 递归下降分析法的设计与实验原理 编译原理

本文内容&#xff1a; 本文章实现的文法&#xff1a; E->T|ET; T->F|T*F; F->i|(E);利用上一篇文章&#xff1a;python 预备实验2 LL(1)文法构造转化后的输出&#xff1a; E->TE; T->FT; F->i|(E); E->TE|; T->*FT|; 手工测试&#xff0c;是LL(1)文…

Flink+Pulsar、Kafka问题分析及方案 -- 事务阻塞

Pulsar、Kafka的事务设计 Pulsar跟Kafka在设计事务功能时&#xff0c;在消费者读取消息的顺序方面&#xff0c;都采用了类似的设计。 比如说&#xff0c;先创建txn1&#xff0c;然后创建txn2&#xff0c;这两个事务生产消息到同一个topic/partition里&#xff0c;但是txn2比tx…

【前端知识】常见的加密算法介绍

【前端知识】常见的加密算法介绍 1 常见的加密算法&#xff08;1&#xff09;哈希函数&#xff08;2&#xff09;对称加密&#xff08;3&#xff09;非对称加密&#xff08;4&#xff09;消息认证码&#xff08;MAC&#xff09; 2.总结 1 常见的加密算法 略微介绍一下前端中常…

Kerberos

序言 kerberos 除了说帮我们验证Java程序是否具有权限来请求Hadoop的服务,也可以来帮助我们检查新增的节点是是否是真实的节点,还是黑客为了套取数据的节点. 比如为HDFS新增一个DataNode节点,如果没有Kerberos验证, 随便一个节点只要连接上NameNode就会存储数据,黑客就可以获…

LeetCode:23. 合并 K 个升序链表

23. 合并 K 个升序链表 1&#xff09;题目2&#xff09;过程3&#xff09;代码1. 最开始2.初步优化 4&#xff09;结果1. 最开始2. 初步优化 1&#xff09;题目 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合…

机器学习基础认识(一)

机器学习应用 机器学习的应用&#xff0c;主要分为两类&#xff1a;预测、分类 预测&#xff0c;一般是指&#xff1a;根据数据&#xff0c;预测数值 分类&#xff0c;一般是指&#xff1a;根据数据&#xff0c;进行分类 预测与分类的关系【个人理解】 分类&#xff0c;本质…

零基础怎么入门网络安全?看这篇就够啦!

由于我之前写了不少网络安全技术相关的故事文章&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人在微信里问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff1f;要学哪些东西&#xff1f;有哪些方向&#xff1f;怎么选&#xff…

Centos7.6部署postgresql15主从

目录 安装pg15&#xff08;master和standby&#xff09;主数据库配置(master)初始化数据库创建归档日志目录设置数据库访问权限修改数据库配置文件开启数据库 从数据库配置(standby)同步主库的数据文件创建文件standby.signal启动从数据库 主从状态验证master上验证standby上验…

H5性能测试怎么做?这些关键指标你得搞清楚

目录 01、Http相关 02、组件是否压缩 03、图片格式和大小是否合适 04、CSS放在顶部 05、JS放在底部 06、JS &CSS压缩 07、是否添加缓存 08、避免非200返回值 09、使用CDN 03、WebView相关 学习资源分享 软件测试面试小程序 01、Http相关 01、Http请求个数 有…

新星计划 Electron+vue2 桌面应用 1 基础

/(ㄒoㄒ)/~~报名了两个新星计划&#xff0c;工作之余写博客…… 另外一个是uniapp的属于个人兴趣&#xff0c;这个桌面应用正好符合工作需要。 活动地址&#xff1a;https://marketing.csdn.net/p/1738cda78d47b2ebb920916aab7c3584 教程地址&#xff1a; 2023新星导师活动…

Java实现PDF导出/预览

网上有很多关于PDF导出的文章&#xff0c;但是个人感觉实现的过于复杂&#xff0c;又是模板又是html的&#xff0c;有的还需要字体模板的支持&#xff0c;本片文章只是实现简单的PDF表格导出&#xff0c;可以实现PDF动态表格导出/预览&#xff0c;这类文章网上很少&#xff0c;…

实践「容器镜像扫描」,Get 云原生应用的正确打开方式

&#x1f31f; 容器技术的兴起&#xff0c;让应用程序更加轻量化和可移植&#xff0c;大大提高了应用交付效率。但容器中的应用也面临各种安全威胁&#xff0c;容器及其镜像安全不可小觑。 近日&#xff0c;在「DevSecOps 软件安全开发实践」课程上&#xff0c;极狐(GitLab) 高…

Linux设置系统时间(上海时区、硬件时间、重启有效)

#查看时间 date#删除当前时区 rm -rf /etc/localtime #修改默认时区为上海 ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime #设置硬件时间 月/日/年 时:分:秒 hwclock --set --date"05/18/2023 17:11:15"#设置系统时间和硬件时间同步 hwclock --hctosys#保…

大数据发展前沿复习

对抗学习 生成对抗网络&#xff08;GAN&#xff09;是非监督式学习的一种方法&#xff0c;透过两个神经网络相互博弈的方式进行学习。生成对抗网络由一个生成网络与一个判别网络组成。生成网络以随机取样作为输入&#xff0c;其输出结果需要尽量模仿训练集中的真实样本。判别网…

vmware17pro安装激活ubuntu22版本最新教程无废话

第一步&#xff1a;下载 下载很方便 官方一键下载链接 第二步 安装 点下一步&#xff0c;一键安装即可&#xff0c;有可能会重启电脑&#xff0c;没关系的&#xff0c;是安全的 第三步&#xff1a;ji活 懂得都懂这是什么 JU090-6039P-08409-8J0QH-2YR7F 4A4RR-813DK-M81A9…

C语言算法--快速排序法

C语言算法–快速排序法 1-什么是快速排序法 快速排序&#xff08;Quicksort&#xff09;是一种常用的排序算法&#xff0c;它基于分治的思想。它的核心思想是选择一个基准元素&#xff0c;将数组划分为两个子数组&#xff0c;使得左边的子数组中的所有元素都小于等于基准元素…