【数据结构】双向带头循环链表

news2024/10/7 11:22:56

文章目录

  • 一、什么是带头双向循环链表
  • 二、带头双向循环链表的实现
    • (一)链表中结构体的声明
    • (二)头节点的创建(链表的初始化)
    • (三)新节点的创建
    • (四)链表的尾插
    • (五)链表的尾删
    • (六)链表的头插
    • (七)链表的头删
    • (八)链表的查找
    • (九)链表节点的修改
    • (十)链表的中间插入(pos前)
    • (十一)链表的中间删除
    • (十二)链表的销毁
  • 总结

一、什么是带头双向循环链表

在【数据结构】单链表中提到,链表为数据结构中的一种存储结构,链表属于线性存储结构,同时链表共有八种,此次所讲的链表为双向带头循环链表,属于链表中结构最复杂的结构;
在这里插入图片描述

带头双向循环链表是链表中带头(哨兵位)、双向、循环三种属性的结合体;
带头即带哨兵位哨兵位只负责存储第一个具有有效数据的节点,本身不存放数据,该处因为为双向循环链表,代表也可访问该链表的尾节点
双向即表示,每个节点不仅能访问该节点的后一个节点,同时也可访问本节点的前一个节点
循环即表示,第一个节点的prev指向尾节点

  • 带头双向循环链表虽然在结构中是所有链表中最为复杂的,但是相比较于单链表的优势在于不需要多次对链表为空进行判断,避免了边界问题
  • 同时如单链表所言,单链表在进行每次尾删或者尾插时,都需要遍历到尾节点且保存尾节点的前一个节点,而对于双向链表而言,只需要访问头节点的prev即可
  • 总而言之,双向带头循环链表而言,虽然在结构上要比单链表复杂的多,但是在操作上确是比单链表而言简单;

二、带头双向循环链表的实现

(一)链表中结构体的声明

因为该链表属于双向循环链表,故需要使用两个指针,一个指针next来存放该节点的下一个节点并使用prev指针来存放该节点的上一个节点
同时,为了避免使用者在使用时需要修改所存储的数据,可以使用typedef对需要存储的数据类型进行重命名方便后期对存储类型的修改

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

#include<assert.h>

typedef int DataType;

typedef struct ListNode {
	struct ListNode* next;
	struct ListNode* prev;
	DataType val;
}LTNode;

(二)头节点的创建(链表的初始化)

该链表为带头双向循环练表,因为存在哨兵位,所以在插入数据之前应给链表安排一个哨兵位,并对哨兵位进行初始化

LTNode* ListCreate()//创建头节点(哨兵位)
{
	LTNode* newnode = BuyNode();
	newnode->next = newnode;
	newnode->prev = newnode;
	return newnode;
}

在对哨兵位进行初始化时同时也要注意该链表的结构为双向循环链表,在只有哨兵位的情况下,哨兵位的next与prev都指向自己

在这里插入图片描述


(三)新节点的创建

新节点的创建也与单链表的相同,唯一不同的就是新创建的节点需要注意双向循环的格式;

LTNode* BuyNode()
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL) {
		perror("BuyNode::malloc");
		return NULL;
	}
	return newnode;
}

(四)链表的尾插

若是在单链表中,尾插需要遍历整个链表找到尾节点,而在该链表中,因为链表头节点(哨兵位)所指向的prev即为尾节点;
故在该链表进行尾插时,只需访问哨兵位的prev所指向的节点

在这里插入图片描述

void ListPushBack(LTNode* phead, DataType x)
{
	assert(phead);

	LTNode* tail = phead->prev;
	LTNode* newnode = BuyNode();
	newnode->val = x;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

(五)链表的尾删

在双链表中,链表的尾删只需释放头节点head内prev所指向的节点(尾节点)即可;

在这里插入图片描述

void ListProBack(LTNode* phead)//尾删
{
	assert(phead);
	LTNode* cur = phead->prev->prev;
	free(cur->next);
	cur->next = phead;
	phead->prev = cur;
}

(六)链表的头插

因为该链表存在哨兵位,故在进行头插时需要插入至头节点(哨兵位)之后;

在这里插入图片描述

void ListPushFront(LTNode* phead, DataType x)//头插
{
	assert(phead);
	LTNode* newnode = BuyNode();
	newnode->val = x;
	LTNode* tail = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = tail;
	tail->prev = newnode;
}

(七)链表的头删

同样的带头双向循环链表在进行头删时只需要删除头节点(哨兵位)后的节点即可;

在这里插入图片描述

void ListProFront(LTNode* phead)//头删
{
	assert(phead);
	LTNode* tail = phead->next->next;
	free(phead->next);
	phead->next = tail;
	tail->prev = phead;
}

(八)链表的查找

双链表的查找,利用while循环对链表进行遍历,循环条件为当指针指向不为哨兵位;
若为哨兵位,则代表链表已经遍历了一次,若是找到对应val的值,则返回节点的地址,否则返回空指针;

LTNode* ListFind(LTNode* phead, DataType x)//查
{
	LTNode* tail = phead->next;
	while (tail->next != phead) {
		if (tail->val == x) {
			return tail;
		}
		tail = tail->next;
	}
	return NULL;
}

(九)链表节点的修改

参数给定节点的地址,通过地址访问该节点所对应的val并对其进行修改;

void ListModify(LTNode* pos, DataType x) //改
{
	assert(pos);
	pos->val = x;
}

(十)链表的中间插入(pos前)

相比较单链表来说,双向链表可以访问所给pos节点的前一个节点,而单链表若是需要进行在pos节点前进行插入则各项消耗都会增大;
参数给定一个pos指针,指针指向为需要插入的节点位置,将新的数据(节点)插入pos所指向节点的prev所指向的位置;

在这里插入图片描述

void ListInsert(LTNode* pos, DataType x) //中间插入(pos前)
{
	assert(pos);
	LTNode* tail = pos->prev;
	LTNode* newnode = BuyNode();
	newnode->val = x;
	pos->prev = newnode;
	newnode->next = pos;
	tail->next = newnode;
	newnode->prev = tail;
}

(十一)链表的中间删除

链表的中间删除则不需要再进行pos之后或者pos之前,双向链表既可以访问pos节点的next,又可访问pos节点的prev;
即在进行中间删除时,可以直接将pos节点进行删除

在这里插入图片描述

void ListErase(LTNode* pos)
{
	LTNode* tail = pos->prev;
	LTNode* cur = pos->next;
	tail->next = cur;
	cur->prev = tail;
	free(pos);
	pos = NULL;
}

(十二)链表的销毁

当使用完空间并不需要再使用时,应及时将空间进行释放,避免出现内存泄漏等问题,在顺序表中,可直接将malloc后的空间进行释放,因为在物理因素上,顺序表为一块连续不断的空间
而链表则只在逻辑上具有连续不断的属性,在物理上本质是一块一块不同位置的空间
双链表的销毁也与单链表的销毁逻辑相同;

void ListDestory(LTNode** pphead)//销毁
{
	LTNode* tail = (*pphead)->next;
	LTNode* cur = NULL;
	while (tail != *pphead) {
		cur = tail->next;
		free(tail);
		tail = cur;
	}
	free(*pphead);
	*pphead = NULL;
}


总结

  • 带头双向循环链表在链表为空的情况下(只有头节点),头节点的next与prev指针都指向自己;
  • 在写双链表的各个接口时应注意应尽量使用断言判断形参所接收的指针是否为有效指针;
  • 使用完内存后应及时将内存还给操作系统(释放);
  • 带头双向循环链表与不带头单向不循环链表为链表中的两个极端,一种为八种链表中结构最复杂的链表;一种为八种链表中结构最简单的链表;但是反而再逻辑中,由于结构的特性,带头双向循环链表的逻辑是几种链表中逻辑最简单的;

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

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

相关文章

( 位运算 ) 231. 2 的幂 ——【Leetcode每日一题】

❓231. 2 的幂 难度&#xff1a;简单 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2 x n 2^x n2x &#xff0c;则认为 n 是 2 的幂次方。 示例 1&…

软考高级架构师笔记1-计算机硬件

目录 1. 前言 & 更新2. CPU组成3. CPU的指令集:4.存储器5. 总线1. 前言 & 更新 注意:绪论不考,直接略过。 计算机硬件章节19-21年没考过,在22年真题考过磁盘调度,根据趋势分析,以后考的概率也不大,了解即可。 本节删掉了第一版中的编码、海明码等内容。 2. CP…

一篇文章搞定《Android异常处理》

------《Android异常处理》 异常种类&#xff08;简述&#xff09;编译时异常运行时异常 运行时的异常和崩溃受检时的异常第一种做法&#xff1a;第二种做法&#xff1a; 不受检时的异常(崩溃Crash)异常的传播崩溃的兜底Looper 循环问题主流程抛出异常问题 安全气囊的实现方案设…

计算机组成原理---第六章总线系统 习题详解版

&#xff08;一&#xff09;课内习题 &#xff08;二&#xff09;课后习题 1.比较单总线、多总线结构的性能特点。 答&#xff1a; &#xff08;1&#xff09; 单总线结构:它是用单一的系统总线连接整个计算 机系统的各大功能部件,各大部件之间的所有的信息传送都通过这组总线…

【企业信息化】第6集 免费开源ERP: Odoo 16 MRP + 维护+ PLM +质量全面生产制造管理

文章目录 一、MRP 物料需求计划1.一款软件&#xff0c;满足您的所有需要2.工作中心控制面板3.优化您的库存等级4.条形码&#xff0c;即开即用5.出色报告关键绩效指标6.与其他Odoo应用程序完全集成 二、PLM 产品生命周期管理1.管理工程变更2.集成文件管理3.智能版本管理4.与其他…

还在为项目初始化、依赖管理问题困扰?Dubbo Initializer 来了!

作者&#xff1a;Dubbo 社区 通过这篇文章&#xff0c;你将学习如何在 1 分钟内用 Dubbo Initializer 模板快速创建 Dubbo Spring Boot 项目&#xff0c;帮你解决项目初始化问题。 什么是 Dubbo Initializer&#xff1f; Dubbo Initializer 是一款帮助开发者快速生成 Dubbo …

【0基础也能学会】JMeter:如何开始简单的WEB压力测试?

背景 最近工作上被安排针对Web网站进行性能压测&#xff0c;以评估特定的硬件配置下Web网站可支持的并发用户数。考虑到JMeter是流行的Web性能压测工具&#xff0c;因此趁着这次机会上网查阅了很多关于JMeter的资料&#xff0c;也自己动手进行软件的配置和调测&#xff0c;从最…

前瞻洞察|借助机器学习,揪出利用DNS隐蔽隧道作恶黑手

黑客会利用DNS协议进行违法犯罪活动&#xff0c;那DNS协议到底是什么&#xff1f;它有何作用&#xff1f;为什么会被选作进行作恶的手段&#xff1f;会造成什么危害&#xff1f;怎么检测及研究现状如何&#xff1f;一连串疑问接踵而至。本篇文章中&#xff0c;我们会为大家一一…

【Java多线程编程】Thread类

Thread类是什么&#xff1f; Thread 类是 Java 提供的一个标准库&#xff0c;我们可以通过 Thread 类进行多线程编程。因此&#xff0c;今天我给大家讲解的是如何使用 Thread 类进行线程编程。 详细讲解 Thread 类中的&#xff1a;lambda 表达式、start 方法&#xff08;启动线…

WiFi(Wireless Fidelity)基础(七)

目录 一、基本介绍&#xff08;Introduction&#xff09; 二、进化发展&#xff08;Evolution&#xff09; 三、PHY帧&#xff08;&#xff08;PHY Frame &#xff09; 四、MAC帧&#xff08;MAC Frame &#xff09; 五、协议&#xff08;Protocol&#xff09; 六、安全&#x…

Cloud Kernel SIG月度动态:发布ANCK 5.10、4.19新版本,ABS新增仓库构建功能

Cloud Kernel SIG&#xff08;Special Interest Group&#xff09;&#xff1a;支撑龙蜥内核版本的研发、发布和服务&#xff0c;提供生产可用的高性价比内核产品。 01 SIG 整体进展 发布 ANCK 5.10-014 版本。 发布 ANCK 4.19-027.2 版本。 ABS 平台新增 OOT 仓库临时构建功…

如何远程控制电脑?3个方法轻松搞定!

案例&#xff1a;如何远程控制电脑&#xff1f; 【我不想时时刻刻都带着自己的电脑&#xff0c;听朋友说可以远程电脑。有没有大神分享一下具体的操作方法&#xff1f;感谢&#xff01;】 随着科技的不断进步&#xff0c;远程控制电脑已经不再是一件难以实现的事情。如今&…

09.python可视化-Seanorn绘制类别关系图boxplot() boxenplot() violinplot()

分类散点图 分类分布图 1). 箱线图 : boxplot() 2).增强箱图boxenplot() 3).小提琴图 :violinplot() 分类统计图 2. 分类分布图 1). 箱线图 应用场景&#xff1a;主要用来显示与类别相关的数据分布。 seaborn.boxplot(xNone, yNone, hueNone, dataNone, orderNone, hue_orde…

GoView 是一个Vue3搭建的低代码数据可视化开发平台

一、总览 开源、精美、便捷的「数据可视化」低代码开发平台 二、整体介绍 框架&#xff1a;基于 Vue3 框架编写&#xff0c;使用 hooks 写法抽离部分逻辑&#xff0c;使代码结构更加清晰&#xff1b; 类型&#xff1a;使用 TypeScript 进行类型约束&#xff0c;减少未知错误…

WiFi(Wireless Fidelity)基础(九)

目录 一、基本介绍&#xff08;Introduction&#xff09; 二、进化发展&#xff08;Evolution&#xff09; 三、PHY帧&#xff08;&#xff08;PHY Frame &#xff09; 四、MAC帧&#xff08;MAC Frame &#xff09; 五、协议&#xff08;Protocol&#xff09; 六、安全&#x…

MySql -- 事务

目录 1.概念 2.事务的运用场景 3.事务的四大特点 4.执行事务带来的问题 4.1 脏读 4.2 不可重复度 4.3 幻读 5. MySQL中事务的隔离级别 1.概念 事务就是把若干个独立操作打包成一个整体而诞生的一种功能. 2.事务的运用场景 比如&#xff1a;A——>B 转账500 A的余额-500…

【Qt编程之Widgets模块】-007:QStandardPaths类使用方法

1 头文件&#xff1a; #include <QStandardPaths>2 详细说明 QStandardPaths类提供用于访问标准路径的方法&#xff0c;该类包含用于查询本地文件系统上的标准位置的函数&#xff0c;用于常见任务&#xff0c;如特定于用户的目录或系统范围的配置目录。 所谓系统标准路…

[pgrx开发postgresql数据库扩展]6.返回序列的函数编写(1)单值序列

上篇文章是中规中矩的标准计算函数&#xff0c;就算不用pgrx&#xff0c;也是可以正常理解的&#xff0c;所以基本上没有什么对于pgrx框架有关系的东西&#xff08;唯一有关系的东西&#xff0c;应该就是Rust的时间类型与pgrx的时间类型的计算了&#xff09;。 这篇文章会讲一…

Java面试(1)Java概述

文章目录 Java 概述1.什么是Java2. JDK1.5 之后的三大版本3. Jdk和Jre和JVM的区别4. 什么是跨平台性&#xff1f;原理是什么5. Java 语言有哪些特点?6. 什么是字节码&#xff1f;采用字节码的最大好处是什么7. 为什么不全部使用 AOT&#xff08;since JDK9&#xff09; 呢&…

马赛克处理

去取马赛克的网址&#xff1a; Redact • Photo - Free And Private Image Redaction In The Browser https://redact.photo/ REDACT.PHOTO &#xff08;照片马赛克处理在线工具&#xff09;简介 REDACT.PHOTO是一个照片马赛克处理在线工具&#xff0c;能够帮助我们非常方便…