数据结构——双向链表dlist

news2025/3/15 5:52:29

前言:大家好😍,本文主要介绍了数据结构——双向链表dlist

一 双向链表定义

1. 双向链表的节点结构

 二 双向链表操作

 2.1 定义

2.2 初始化

2.3 插入

2.3.1 头插

2.3.2 尾插

2.3.3 按位置插

2.4 删除

2.4.1 头删

2.4.2 尾删

2.4.3 按位置删

2.5 其余操作

2.6 主函数测试


一 双向链表定义

双向链表(Doubly Linked List)是一种常见的线性数据结构,与单链表不同,双向链表的每个节点包含两个指针:一个指向前一个节点,一个指向后一个节点。这种结构使得双向链表支持从两个方向遍历,同时在插入和删除操作中更加灵活和高效。 

1. 双向链表的节点结构

双向链表的每个节点包含三个部分:

  1. 数据域:存储数据元素。

  2. 前驱指针(prev:指向当前节点的前一个节点。

  3. 后继指针(next:指向当前节点的后一个节点。

 二 双向链表操作

 2.1 定义

//定义了双向链表的结构体
typedef int ELEM_TYPE;

struct DNode
{
	ELEM_TYPE data;  //数据域
	struct DNode* next; //下一个结点地址
	struct DNode* prior;//上一个
};

typedef struct DNode DNode;
typedef struct DNode* PDNode;

2.2 初始化

void Init_Double_List(struct DNode* plist) //双向链表 第一种struct DNode*
{
	assert(plist != NULL);
	plist->next = NULL;
	plist->prior = NULL;
}

2.3 插入

2.3.1 头插

bool Insert_head(DNode* plist, ELEM_TYPE val)//二DNode*
{
	assert(plist != NULL);
	if (NULL == plist)
	{
		exit(EXIT_FAILURE);
	}

	DNode* pnewnode = (DNode*)malloc(sizeof(DNode));
	if (NULL == pnewnode)
		exit(1);

	pnewnode->data = val;
    //插在辅助接点后面

	pnewnode->next = plist->next;
	pnewnode->prior = plist;

	if (!Is_Empty(plist))
	{
		pnewnode->next->prior = pnewnode;
		//这个也可以plist->next->prior = pnewnode;
	}

	plist->next = pnewnode;
	return true;

}

2.3.2 尾插

bool Insert_tail(PDNode plist, ELEM_TYPE val)//三PDNode
{
	assert(NULL !=plist );
	if (NULL == plist)
	{
		exit(EXIT_FAILURE);
	}

	//购买新节点
	DNode* pnewnode = (DNode*)malloc(sizeof(DNode));
	if (NULL == pnewnode)
		exit(1);
	pnewnode->data = val;

	//找到合适的待插入位置需要让一个临时指针p指向尾结点
	DNode* p = plist;
	while (p->next != NULL)
		p = p->next;

	//插入三行代码:只有三个指针域需要修改231	
	pnewnode->next = p->next;//null
	pnewnode->prior = p;
	p->next = pnewnode;

	return true;

}

2.3.3 按位置插

bool Insert_pos(DNode* plist, ELEM_TYPE val, int pos)
{
	assert(NULL != plist);
	if (NULL == plist)
	{
		exit(EXIT_FAILURE);
	}
	//对pos合法性断言
	assert(pos >= 0 && pos <= Get_length(plist));
	

    //对pos判断 //pos=0 直接return头插函数
               //pos=length return尾插函数
    if (pos == 0)
	{
		return Insert_head(plist, val);
	}
	else if(pos==Get_length(plist))
	{
		return Insert_tail(plist, val);
	}
	

    //剩下的pos都是合法且是中间位置,需要修改四个指针域
	//找到合适的插入位置让p指向待插入位置的上一个结点
	else
	{
		DNode* pnewnode = (DNode*)malloc(sizeof(DNode));

		if (NULL == pnewnode)
			exit(1);
		pnewnode->data = val;

		DNode* p = plist;
		while (p->next != NULL)
			p = p->next;

		//插入四行代码 因为有四个指针域要修改
		pnewnode->next = p->next;
		pnewnode->prior = p;
		pnewnode->next->prior = pnewnode;
		p->next = pnewnode;

		return true;
	}
}

2.4 删除

2.4.1 头删

bool Del_head(DNode* plist)
{
	//判空
	assert(NULL != plist);
	if (NULL == plist)
	{
		exit(EXIT_FAILURE);
	}

	//判断链表是否为空
	if (Is_Empty(plist))
		return false;

	//申请俩个临时指针p和q,分别指向删除结点前驱和删除结点本身
	DNode* p = plist;//可以不要这行
	DNode* q = p->next;

	//区分情况 若是只有唯一一个有效结点 则只要需要修改一个指针域
	//              >1则修改俩个指针域
	if (Get_length(plist)>1)
	{
		q->next->prior = p;
	}


	//跨越指向+释放
	p->next = q->next;
	free(q);
	q = NULL;
	return true;

}

2.4.2 尾删

bool Del_tail(DNode* plist)
{
	assert(NULL != plist);
	if (NULL == plist)
	{
		exit(EXIT_FAILURE);
	}

	//对双向链表判空
	if (Is_Empty(plist))
		return false;

	//辅助接点后有结点就可以尾删 
	//申请俩个临时指针p和q,分别指向删除结点前驱和删除结点本身
	DNode* p = plist;
	while (p->next->next != NULL)
		p = p->next;
	DNode* q = p->next;

	//跨越指向+释放
	p->next = q->next;
	free(q);
	q = NULL;
	return true;
}

2.4.3 按位置删

2.5 其余操作

//9.判空
bool Is_Empty(DNode* plist)
{
	return plist->next == NULL;
}

//10获取有效值长度
int Get_length(DNode* plist)
{
	int count = 0;
	DNode* p = plist->next;
	for (; p != NULL;p=p->next)
	{
		count++;
	}
	return count;
}

//11.清空
void Clear(DNode* plist)
{
	Destroy1(plist);
}

//12.销毁1(需要辅助节点参与进来)
void Destroy1(DNode* plist)
{
	//无限头删
	while (plist->next != NULL)
	{
		Del_head(plist);
	}
}

//13.销毁2(不需要辅助节点参与进来)相当于单链表的销毁
void Destroy2(DNode* plist)
{
	//不借助头结点
	//要用俩个指针pq去搭配 销毁所有结点
	//0.assert  head  

	//1.申请指针p,让其指向第一个有效结点
	DNode* p =plist->next;//p有可能为NULL, 有可能不空

	//2.申请指针q,先不给q赋值
	DNode* q = NULL; //因为p有可能是NULL,所以不能现在直接把p->next给到q

	//3.反复通过p和q打配合,去销毁后续节点
	while (p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}

	//4.节点全部销毁完毕,别忘了把辅助节点的指针域置空
	plist->next = NULL;//这一行代码可以帮助,下一次启用的时候,辅助节点不用初始化了
}

//14.打印
void Show(DNode* plist)
{
	DNode* p = plist->next;
	for (; p != NULL;p=p->next)
	{
		printf("%d", p->data);
	}
	printf("\n");
}

2.6 主函数测试

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

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

相关文章

IDEA 一键完成:打包 + 推送 + 部署docker镜像

1、本方案要解决场景&#xff1f; 想直接通过本地 IDEA 将最新的代码部署到远程服务器上。 2、本方案适用于什么样的项目&#xff1f; 项目是一个 Spring Boot 的 Java 项目。项目用 maven 进行管理。项目的运行基于 docker 容器&#xff08;即项目将被打成 docker image&am…

图像分类数据集

《动手学深度学习》-3.5-学习笔记 # 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式&#xff0c; # 并除以255使得所有像素的数值均在0&#xff5e;1之间 trans transforms.ToTensor()#用于将图像数据从 PIL 图像格式&#xff08;Python Imaging Library&#xff…

设计模式之美

UML建模 统一建模语言&#xff08;UML&#xff09;是用来设计软件的可视化建模语言。它的语言特点是简单 统一 图形化 能表达软件设计中的动态与静态信息。 UML的分类 动态结构图&#xff1a; 类图 对象图 组件图 部署图 动态行为图&#xff1a; 状态图 活动图 时序图 协作…

2025-03-15 学习记录--C/C++-PTA 练习3-4 统计字符

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、题目描述 ⭐️ 练习3-4 统计字符 本题要求编写程序&#xff0c;输入10个字符&#xff0c;统计其中英文字母、空格或回车、…

802.11标准

系列文章目录 文章目录 系列文章目录一、相关知识二、使用步骤1.802.11修正比较2.802.11ac 三、杂记 一、相关知识 跳频扩频&#xff1a;射频信号可分为窄带信号和扩频信号。如果射频信号的带宽大于承载数据所需的带宽&#xff0c;该信号就属于扩频信号。跳频扩频(FHSS)是一种…

母婴商城系统Springboot设计与实现

项目概述 《母婴商城系统Springboot》是一款基于Springboot框架开发的母婴类电商平台&#xff0c;旨在为母婴产品提供高效、便捷的在线购物体验。该系统功能全面&#xff0c;涵盖用户管理、商品分类、商品信息、商品资讯等核心模块&#xff0c;适合母婴电商企业或个人开发者快…

C#通过API接口返回流式响应内容---分块编码方式

1、背景 上一篇文章《C#通过API接口返回流式响应内容—SSE方式》阐述了通过SSE&#xff08;Server Send Event&#xff09;方式&#xff0c;由服务器端推送数据到浏览器。本篇是通过分块编码的方式实现 2、效果 3、具体代码 3.1 API端实现 [HttpGet] public async Task Chu…

游戏引擎学习第158天

回顾和今天的计划 我们在这里会实时编码一个完整的游戏&#xff0c;没有使用引擎或库&#xff0c;一切都由我们自己做所有的编程工作&#xff0c;游戏中的每一部分&#xff0c;无论需要做什么&#xff0c;我们都亲自实现&#xff0c;并展示如何完成这些任务。今天&#xff0c;…

[新能源]新能源汽车快充与慢充说明

接口示意图 慢充接口为交流充电口&#xff08;七孔&#xff09;&#xff0c;快充接口为直流充电口&#xff08;九孔&#xff09;。 引脚说明 上图给的是充电口的引脚图&#xff0c;充电枪的为镜像的。 慢充接口引脚说明 快充接口引脚说明 充电流程 慢充示意图 慢充&…

在IDEA中连接达梦数据库:详细配置指南

达梦数据库&#xff08;DM Database&#xff09;作为国产关系型数据库的代表&#xff0c;广泛应用于企业级系统开发。本文将详细介绍如何在IntelliJ IDEA中配置并连接达梦数据库&#xff0c;助力开发者高效完成数据库开发工作。 准备工作 1. 下载达梦JDBC驱动 访问达梦官方资…

基于yolov8+streamlit实现目标检测系统带漂亮登录界面

【项目介绍】 基于YOLOv8和Streamlit实现的目标检测系统&#xff0c;结合了YOLOv8先进的目标检测能力与Streamlit快速构建交互式Web应用的优势&#xff0c;为用户提供了一个功能强大且操作简便的目标检测平台。该系统不仅具备高精度的目标检测功能&#xff0c;还拥有一个漂亮且…

软件性能测试与功能测试联系和区别

随着软件开发技术的迅猛发展&#xff0c;软件性能测试和功能测试成为了确保软件质量的两个重要环节。那么只有一字之差的性能测试和功能测试分别是什么?又有哪些联系和区别呢? 一、软件性能测试是什么?   软件性能测试是为了评估软件系统在特定条件下的表现&#xff0c;包…

Axure设计之堆叠柱状图教程(中继器)

堆叠柱状图是一种常用的数据可视化工具&#xff0c;它通过在同一柱状图内堆叠不同类别的数据&#xff0c;以展示每个类别在总体中的贡献或占比。堆叠柱状图不仅可以帮助我们观察数据的总量&#xff0c;还能清晰地揭示各部分之间的关系和变化趋势。以下是一个使用Axure制作动态效…

本地部署Hive集群

规划 服务机器Hive本体部署在Node1元数据服务所需的关系型数据库(MYSQL)部署在Node1 安装MYSQL数据库 # 更新密钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022# 安装Mysql yum库 rpm -Uvh http://repo.mysql.com//mysql57-community-release-el7-7.noarch.…

零成本本地化搭建开源AI神器LocalAI支持CPU推理运行部署方案

文章目录 前言1. Docker部署2. 简单使用演示3. 安装cpolar内网穿透4. 配置公网地址5. 配置固定公网地址 前言 嘿&#xff0c;小伙伴们&#xff01;今天给大家带来一个超酷的黑科技——LocalAI。没错&#xff0c;你没听错&#xff0c;就是那个能在你的个人电脑上运行大型语言模…

git使用命令总结

文章目录 Git 复制创建提交步骤Git 全局设置:创建 git 仓库:已有仓库? 遇到问题解决办法&#xff1a;问题一先git pull一下&#xff0c;具体流程为以下几步&#xff1a; 详细步骤 Git 复制 git clone -b RobotModelSetting/develop https://gitlab.123/PROJECT/123.git创建提…

内容中台的核心架构是什么?

模块化架构设计解析 内容中台的模块化架构通过分层解耦实现灵活扩展&#xff0c;其核心由基础资源层、能力服务层与业务应用层构成。基础层以统一数据治理体系为支撑&#xff0c;通过标准化接口实现结构化与非结构化数据的统一存储&#xff0c;例如Baklib采用分布式存储架构保…

bootloader相关部分

简单说明 程序烧录的方式主要有ICP,ISP,IAP 其中ICP就是常用的jlink等工具 ISP就是利用MCU自带的一些特殊引脚烧录&#xff0c;比如uart IAP就是利用用户写的bootloader代码烧录 bootloader主要分为三层&#xff0c;厂家出厂的bootrom ,用户自己写的bootloader&#xff0c;…

AI+视频监控电力巡检:EasyCVR视频中台方案如何赋能电力行业智能化转型

随着电力行业的快速发展&#xff0c;电力设施的安全性、稳定性和运维效率变得至关重要。传统视频监控系统在实时性、智能化及多系统协同等方面面临严峻挑战。EasyCVR视频中台解决方案作为一种先进的技术手段&#xff0c;在电力行业中得到了广泛应用&#xff0c;为电力设施的监控…

C++从入门到入土(七)——多态

目录 前言 多态的概念 多态的定义 虚函数的介绍 虚函数的重写/覆盖 析构函数的重写 override和final关键字 纯虚函数和抽象类 重写/重载/隐藏总结 多态的原理 小结 前言 C一共有三个特性&#xff0c;封装、继承和多态&#xff0c;在前面的文章中&#xff0c;我们分别…