带头+双向+循环链表

前言:

前面我们已经学习了单链表的结构及其功能特点,也了解了单链表在实现一些功能时出现的一些缺点,比如在删除某个节点前面一个节点时就需要再开一个变量来存放前面一个节点的信息,这样就显得不灵活,为了使链表实现功能更加灵活,我给来介绍带头双向循环链表,这种链表可以看成是单链表的升级版。

什么是带头双向循环链表?

带头双向循环链表是一种常见的链表数据结构,它是由若干个结点组成的链式数据结构,每个结点包含两个指针,分别指向前一个结点和后一个结点。与普通的单向链表只能单向遍历不同,在双向循环链表中,可以使用前后两个方向进行遍历

带头双向循环链表的优点?

带头链表意味着在链表头部添加一个空的头结点,这个头结点不包含数据,仅包含指向链表首尾结点的指针,它主要作用是简化链表的操作。在插入、删除、查找等操作时,可以直接从头结点开始操作,无需特殊处理首尾结点

双向循环意味着链表的每一个节点都可以随意的找到上一个节点以及下一个节点,并且头节点的前驱指针指向尾节点,尾节点的后驱指针指向头节点,这样整个链表就形成了一个环状结构,可以任意方向遍历整个链表

由于带头双向循环链表具有链表的优点,同时又兼具循环队列的特点,因此在实际应用中被广泛使用。比如在实现 LRU 缓存淘汰算法、模拟双向队列等场景中都有使用。

代码实现

DList.h头文件

各种功能函数的声明以及数据的定义:

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

// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;

// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* phead);
// 双向链表打印
void ListPrint(ListNode* phead);
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* phead);
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* phead);
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

DList.c文件

各种功能函数的具体实现细节:

#include"DList.h"

// 创建返回链表的头结点.
ListNode* ListCreate() {
	ListNode* NewList = (ListNode*) malloc(sizeof(ListNode));
	if (NewList == NULL) {
		perror("malloc: fail");
	}

	NewList->_data = -1;
	NewList->_prev = NewList;
	NewList->_next = NewList;
	return NewList;
}

//创造节点
static ListNode* CreatNode(LTDataType x) {
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL) {
		perror("malloc:fail");
	}
	newnode->_data = x;
	newnode->_next = newnode;
	newnode->_prev = newnode;
	return newnode;
}

// 双向链表销毁
void ListDestory(ListNode* phead) {
	assert(phead);
	ListNode* cur = phead;
	cur = cur->_next;
	while (cur!=phead) {
		ListNode* temp = cur->_next;
		cur->_prev->_next = cur->_next;
		cur->_next->_prev = cur->_prev;
		free(cur);
		cur = temp;
	}
	free(cur);
	cur = NULL;
}

// 双向链表打印
void ListPrint(ListNode* phead) {
	assert(phead);
	ListNode* cur = phead->_next;
	printf("%d<=>", phead->_data);
	while (cur!= phead) {
		printf(" %d <=>", cur->_data);
		cur = cur->_next;
	}
	printf("%d\n", phead->_data);
}

// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x) {
	assert(phead);
	ListNode* newnode = CreatNode(x);
	ListNode* tail = phead->_prev;
	tail->_next = newnode;
	newnode->_prev = tail;
	newnode->_next = phead;
	phead->_prev = newnode;
}
// 双向链表尾删
void ListPopBack(ListNode* phead) {
	assert(phead&&(phead->_next!=phead));

	ListNode* tail = phead->_prev;
	phead->_prev = tail->_prev;
	tail->_prev->_next = phead;
	free(tail);
	tail = NULL;
}
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x) {
	assert(phead);
	ListNode* newnode = CreatNode(x);
	phead->_next->_prev = newnode;
	newnode->_next = phead->_next;
	newnode->_prev = phead;
	phead->_next = newnode;
}

// 双向链表头删
void ListPopFront(ListNode* phead) {
	assert(phead&&(phead->_next!=phead));
	ListNode* head = phead->_next;
	phead->_next->_next->_prev = phead;
	phead->_next = phead->_next->_next;
	free(head);
	head = NULL;
}

// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x) {
	assert(phead && (phead->_next != phead));
	//ListNode* newnode = CreatNode(x);
	ListNode* cur = phead->_next;
	while (cur != phead) {
		if (cur->_data == x) {
			return cur;
		}
		cur = cur->_next;
	}
	return NULL;
}

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x) {
	assert(pos);
	ListNode* newnode = CreatNode(x);

	pos->_prev->_next = newnode;
	newnode->_prev = pos->_prev;
	newnode->_next = pos;
	pos->_prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos) {
	assert(pos&&(pos->_next!=pos));
	ListNode* temp = pos;
	pos->_prev->_next = pos->_next;
	pos->_next->_prev = pos->_prev;
	free(temp);
	temp = NULL;
}

test.c测试文件

分块测试各个部分的功能

#include"DList.h"

void test1()//测试尾插、尾删
{
	// 创建返回链表的头结点.
	ListNode* phead=NULL;
	phead= ListCreate();
	ListPushBack(phead, 1);//尾插
	ListPushBack(phead, 2);
	ListPushBack(phead, 3);
	ListPrint(phead);
	printf("尾删\n");
	ListPopBack(phead);//尾删
	ListPrint(phead);

	printf("尾删\n");
	ListPopBack(phead);//尾删
	ListPrint(phead);
	//ListPopBack(phead);
	//ListPrint(phead);
	ListDestory(phead);//销毁链表
}


void test2()//测试头插、头删
{
	// 创建返回链表的头结点.
	ListNode* phead = NULL;
	phead = ListCreate();
	ListPushFront(phead, 1);//头插,头删
	ListPushFront(phead, 2);
	ListPushFront(phead, 3);
	ListPrint(phead);
	printf("头删\n");
	ListPopFront(phead);
	ListPrint(phead);
	printf("头删\n");
	ListPopFront(phead);
	//ListPopFront(phead);
	//ListPopFront(phead);
	//ListPopFront(phead);
	ListPrint(phead);
	ListDestory(phead);

}
void test3()//测试查找,随机位置插入,随机位置删除
{
	// 创建返回链表的头结点.
	ListNode* phead = NULL;
	phead = ListCreate();//初始化链表数据 1 2 3 4 5
	ListPushFront(phead, 1);
	ListPushFront(phead, 2);
	ListPushFront(phead, 3);
	ListPushFront(phead, 4);
	ListPushFront(phead, 5);
	ListPrint(phead);
	//ListNode* targetnode = ListFind(phead, 5);
	ListNode* targetnode = ListFind(phead, 4);
	

	if (targetnode == NULL) {//没有找到
		printf("没有找到\n");
	}
	else {
		printf("找到%d了\n",targetnode->_data);
		/*ListErase(targetnode);
		printf("删除这个节点\n");*/
		ListInsert(targetnode, 10);//在目标节点前面插入
		printf("在这个节点插入一个数\n");
		ListPrint(phead);
	}
	ListNode* targetnode2 = ListFind(phead, 4);
	if (targetnode2 == NULL) {//没有找到
		printf("没有找到\n");
	}
	else {
		printf("找到%d了\n", targetnode2->_data);
		ListErase(targetnode);
		printf("删除这个节点\n");
		ListPrint(phead);
	}
	//ListPopFront(phead);
	//ListPrint(phead);
	ListDestory(phead);
	phead = NULL;
}

int main() {
	test1();
	//test2();
	//test3();
	return 0;
}

test1运行结果

在这里插入图片描述

test2运行结果

在这里插入图片描述

test3运行结果

在这里插入图片描述

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

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

相关文章

“可一学院”新课程《区块链企业应用》正式上线

2023年8月&#xff0c;上海可一澈科技有限公司启动了一站式区块链学习平台“可一学院BitClass”。9月6日&#xff0c;可一学院正式推出一门新课程《区块链企业应用》&#xff0c;这门课程将帮助学习者了解企业需要什么样的区块链&#xff0c;以及应该如何运用这项技术来推动自身…

GIT的安装与常见命令

Git的介绍 Git是一个开源的分布式版本控制系统&#xff0c;最初由Linus Torvalds在2005年创建用于管理Linux内核的开发&#xff0c;现在已成为全球最流行的版本控制工具之一。 Git可以跟踪代码的修改&#xff0c;记录开发历程&#xff0c;保证多人合作开发时代码的一致性&…

5个写自定义函数小练习

计算列表平均值、素数判定、反转字符串&#xff0c;查找整数列表最大最小值、统计字符串中元音字母个数(大小写字不敏感)。 (笔记模板由python脚本于2023年11月09日 21:50:35创建&#xff0c;本篇笔记适合熟悉Python函数及基本数据类型的coder翻阅) 【学习的细节是欢悦的历程】…

使用反射来遍历Java对象类中的所有属性名和属性值

有些时候我们需要获取到一个对象中的所有属性名和属性值&#xff0c;对其值进行操作&#xff0c;例如判断对象中某个属性是否是空值。 这种时候我们再使用get(),set()来进行操作就会有些麻烦了。 因此我们可以选择使用反射来进行遍历对象中的所有属性名和属性值。在遍历中编写逻…

陪玩2.0升级版源码/价值18500元的最新商业版游戏陪玩语音聊天系统源码

陪玩2.0升级版源码&#xff0c;价值18500元的最新商业版游戏陪玩语音聊天系统源码。 修复部分逻辑以及bug 修复bug&#xff1a;店员拒单后&#xff0c;退款会退到店员账号里而不是用户账户里。 修复bug&#xff1a;客户在盲盒下单后&#xff0c;马上取消了订单&#xff0c;但…

[100天算法】-定长子串中元音的最大数目(day 67)

题目描述 给你字符串 s 和整数 k 。请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。英文中的 元音字母 为&#xff08;a, e, i, o, u&#xff09;。示例 1&#xff1a;输入&#xff1a;s "abciiidef", k 3 输出&#xff1a;3 解释&#xf…

<C++> list模拟实现

目录 前言 一、list的使用 1. list的构造函数 2. list iterator的使用 3. list capacity 4. list modifiers 5. list的算法 1. unique​ 2. sort 3. merge 4. remove 5. splice 二、list模拟实现 1. 设置节点类 && list类 2. push_back 3. 迭代器 重载 * 重载前置 …

os_cfg.h、os_cpu.h和ucos_ii.h

目录 文件组织代码研读#ifndef OS_CFG_H#if OS_TASK_STAT_EN > 0u 文件组织 os_cfg.h 用于定义操作系统&#xff08;OS&#xff09;的配置参数&#xff0c;例如任务数量、堆栈大小、时间片大小等。它通常包含了用户可以根据需求进行配置的宏定义。os_cpu.h 用于定义与特定CP…

Centos批量删除系统重复进程

原创作者&#xff1a;运维工程师 谢晋 Centos批量删除系统重复进程 客户一台CENTOS 7系统负载高&#xff0c;top查看有很多sh的进程&#xff0c;输入命令top -c查看可以看到对应的进程命令是/bin/bash     经分析后发现是因为该脚本执行时间太长&#xff0c;导致后续执…

11-09 周四 CNN 卷积神经网络基础知识

11-09 周四 CNN 卷积神经网络 时间版本修改人描述2023年11月9日09:38:12V0.1宋全恒新建文档 简介 学习一下CNN&#xff0c;卷积神经网络。使用的视频课程。视觉相关的任务&#xff1a; 人脸识别 卷积网络与传统网络的区别&#xff1a; <img altimage-20231109094400591 s…

《011.SpringBoot+vue之汽车销售管理系统》

《011.SpringBootvue之汽车销售管理系统》 项目简介 [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMybatis; 前台&#xff1a;vueElementUI; [2]功能模块展示&#xff1a; 1.登录 2.销…

MySQL表的增删改查(进阶)

1. 数据库约束 1.1 约束类型 NOT NULL - 指示某列不能存储 NULL 值。 UNIQUE - 保证某列的每行必须有唯一的值。 DEFAULT - 规定没有给列赋值时的默认值。 PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列&#xff08;或两个列多个列的结合&#xff09;有唯一标识&#…

Go并发编程(上)

目录 一、go语言当中的协程 二、MPG模型介绍 三、Goroutine 的使用 3.1 协程的开启 3.2 优雅地等待子协程结束 四、捕获子协程的panic 五、管道Channel 5.1、认识管道 5.2、Channel的遍历和关闭 5.3 、用管道实现生产者消费者模型 5.4、Channel一些使用细节和注意事…

交叉编译中常见错误解决方法

目录 程序运行基础知识 编译程序时去哪找头文件&#xff1f; 链接时去哪找库文件&#xff1f; 运行时去哪找库文件&#xff1f; 运行时不需要头文件&#xff0c;所以头文件不用放到板子上 常见错误的解决方法 头文件问题 库文件问题 运行问题 交叉编译程序的万能命令 …

开源论道 源聚一堂@COSCon

自2015年以来&#xff0c;开源高峰论坛一直是中国开源年会中的传统亮点项目。本次在COSCon23 大会期间的高峰圆桌会&#xff0c;于2023年10月29日在成都高新区的菁蓉汇召开。 本次高峰圆桌上&#xff0c;我们特别邀请了20 位来自企业&#xff0c;基金会和社区的专家和领袖参加讨…

基于单片机设计的智能风扇(红外线无线控制开关调速定时)

一、项目介绍 在炎热的夏季&#xff0c;风扇成为人们室内生活中必不可少的电器产品。然而&#xff0c;传统的风扇控制方式存在一些不便之处&#xff0c;比如需要手动操作开关、无法远程控制和调速&#xff0c;以及缺乏定时功能等。为了解决这些问题&#xff0c;设计了一款基于…

Python实验项目6 :文件操作与模块化

1、使用random库&#xff0c;产生10个100到200之间的随机数&#xff0c;并求其最大值、平均值、标准差和中位数。 # 1、使用random库&#xff0c;产生10个100到200之间的随机数&#xff0c;并求其最大值、平均值、标准差和中位数。 import random # 定义一个列表 list[] for i …

关于Android Studio中开发Flutter配置

配置系统环境变量&#xff1a;path下 &#xff0c;flutter的bin目录下 File->Settings->Languages&Frameworks->FlutterFile->Settings->Languages&Frameworks->DartFile->Settings->Languages&Frameworks->Android SDK 确认是…

QMetaType和QVariant使用

描述 QMetaType和QVariant可以结合使用&#xff0c;用于在运行时确定数据类型。 QMetaType是Qt提供的用于管理各种数据类型的类&#xff0c;它可以帮助我们在运行时动态地创建、销毁、复制和比较数据类型。我们可以使用QMetaType来注册我们自己的数据类型&#xff0c;并为其提…

ChromeDriver谷歌浏览器驱动下载安装与使用最新版118/119/120

ChromeDriver谷歌浏览器驱动下载安装与使用最新版118/119/120 1. 确定Chrome版本 我们首先确定自己的Chrome版本 Chrome设置->关于Chrome 可以看到&#xff0c;当前chrome是最新版本&#xff1a;119.0.6045.124&#xff08;正式版本&#xff09; &#xff08;64 位&#…