【数据结构 05】双链表

news2024/11/14 21:13:46

一、原理

双链表又称双向链表,通常情况下是带头循环结构,在C++STL标准模板库中封装的<list.h>头文件就是带头双向循环链表。

特性:增删灵活且高效,支持随机增删但不支持随机访问

设计思路:

  1. 链表包含一个头节点head,不存储数据,用于链表的维护,提高数据增删效率
  2. 每一个链表节点Node都包含一个数据和两个指针(前驱指针prev和后继指针next)
  3. 前驱指针prev指向前一个节点,后继指针next指向后一个节点
  4. 当链表为空时,头结点head的prev指针和next指针都指向head自身
  5. 节点的增删通过前后指针指向的改变即可完成,无需数据移动,效率高

二、DoubleList.h

#define _CRT_SECURE_NO_WARNINGS 1

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

typedef int DataType;

typedef struct Node
{
	DataType data;
	struct Node* next;
	struct Node* prev;
}Node;

typedef struct List
{
	Node* head;
}List;

void Init(List* plist)
{
	plist->head = (Node*)malloc(sizeof(Node));
	plist->head->prev = plist->head;
	plist->head->next = plist->head;
}

bool Empty(List* plist)
{
	return plist->head == plist->head->next;
}

Node* BuyNode(DataType x)
{
	Node* node = (Node*)malloc(sizeof(Node));
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
}

void PushFront(List* plist, DataType x)
{
	Node* node = BuyNode(x);

	if (Empty(plist))
	{
		node->next = plist->head;
		node->prev = plist->head;
		plist->head->next = node;
		plist->head->prev = node;
	}
	else
	{
		Node* next = plist->head->next;
		node->prev = plist->head;
		node->next = next;
		next->prev = node;
		plist->head->next = node;
	}
}

void PushBack(List* plist, DataType x)
{
	Node* node = BuyNode(x);

	if (Empty(plist))
	{
		node->next = plist->head;
		node->prev = plist->head;
		plist->head->next = node;
		plist->head->prev = node;
	}
	else
	{
		Node* prev = plist->head->prev;
		node->next = plist->head;
		node->prev = prev;
		prev->next = node;
		plist->head->prev = node;
	}
}

void PopFront(List* plist)
{
	if (Empty(plist))
	{
		printf("双链表为空,头删失败\n");
		return;
	}

	Node* cur = plist->head->next;
	plist->head->next = cur->next;
	cur->next->prev = plist->head;

	free(cur);
	cur = NULL;
}

void PopBack(List* plist)
{
	if (Empty(plist))
	{
		printf("双链表为空,尾删失败\n");
		return;
	}

	Node* cur = plist->head->prev;
	plist->head->prev = cur->prev;
	cur->prev->next = plist->head;

	free(cur);
	cur = NULL;
}

Node* Find(List* plist, DataType x)
{
	Node* cur = plist->head->next;
	while (cur != plist->head)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

void InsertFront(Node* pos, DataType x)
{
	if (pos == NULL)
	{
		printf("pos为空,插入失败\n");
		return;
	}

	Node* node = BuyNode(x);

	Node* prev = pos->prev;
	prev->next = node;
	node->prev = prev;
	node->next = pos;
	pos->prev = node;
}

void Delete(Node* pos)
{
	if (pos == NULL)
	{
		printf("pos为空,Delete失败\n");
		return;
	}
	
	Node* next = pos->next;
	Node* prev = pos->prev;

	next->prev = prev;
	prev->next = next;

	free(pos);
	pos = NULL;
}

void Destroy(List* plist)
{
	while (!Empty(plist))
	{
		PopFront(plist);
	}
	free(plist->head);
	plist->head = NULL;
	printf("双链表销毁成功\n");
}

void Print(List* plist)
{
	if (plist->head == NULL)
	{
		printf("双链表不存在\n");
		return;
	}

	Node* cur = plist->head->next;
	printf("head -> ");
	while (cur != plist->head)
	{
		printf("%2d -> ", cur->data);
		cur = cur->next;
	}
	printf("head\n");
}

三、test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "DoubleList.h"

int main()
{
	List list;
	Init(&list);
	Print(&list);	// head -> head

	// 尾插数据
	PushBack(&list, 1);
	PushBack(&list, 3);
	PushBack(&list, 5);
	PushBack(&list, 7);
	Print(&list); // head -> 1 -> 3 -> 5 -> 7 -> head

	// 头插数据
	PushFront(&list, 2);
	PushFront(&list, 4);
	PushFront(&list, 6);
	PushFront(&list, 8);
	Print(&list); // head -> 8 -> 6 -> 4 -> 2 -> 1 -> 3 -> 5 -> 7->head

	// 尾删数据
	PopBack(&list);
	PopBack(&list);
	PopBack(&list);
	Print(&list); // head -> 8 -> 6 -> 4 -> 2 -> 1 -> head

	// 头删数据
	PopFront(&list);
	PopFront(&list);
	PopFront(&list);
	Print(&list); // head -> 2 -> 1 -> head

	// 在查询的节点前插入数据
	InsertFront(Find(&list, 1), 11);
	InsertFront(Find(&list, 11), 111);
	Print(&list); // head -> 2 -> 111 -> 11 -> 1 -> head

	// 删除查询的节点
	Delete(Find(&list, 1));
	Delete(Find(&list, 11));
	Delete(Find(&list, 111));
	Print(&list); // head -> 2 -> head

	// 销毁链表
	Destroy(&list); // 链表销毁成功
	Print(&list); // 链表不存在
	return 0;
}

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

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

相关文章

webassembly003 whisper.cpp的main项目-1

参数设置 /home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug/bin/main options:-h, --help [default] show this help message and exit-t N, --threads N [4 ] number of threads to use during computation-p N, --processors …

Linux:进度条的创建

目录 使用工具的简单介绍&#xff1a; \r &#xff1a; fflush &#xff1a; 倒计时的创建&#xff1a; 倒计时的工作原理&#xff1a; 进度条的创建&#xff1a; 不同场景下、打印任意长度的进度条&#xff1a; main .c procbor.c 测试效果&#xff1a; 使用工具…

EasyExcel通用导入 | 简单封装

0. 前言&#xff1a;1. 基本思路&#xff1a;2. 调用代码&#xff1a; 0. 前言&#xff1a; 之前做了好几个导入&#xff0c;用EasyExcel每次都要定义监听器去处理&#xff0c;就想能不能做个通用的方式&#xff0c;如下 1. 基本思路&#xff1a; 导入无非主要就是参数校验和数…

TypeScript(八) number和string

1. TypeScript number 1.1. 描述 Number对象是原始数值的包装对象。 1.2.语法 var num new Number(value);;注意&#xff1a;如果一个参数值不能转换为一个数字&#xff0c;将返回NaN&#xff08;非数字值&#xff09;。 1.3. 对象属性 属性描述MAX_VALUE可表示的最大的数…

阿尔泰科技—创新解决农业环境监测难题!

引言 “农业是人类生活中不可或缺的重要领域&#xff0c;而农业环境的监测与保护对于农作物的生长和农业生产的可持续发展至关重要。为了解决农业环境参数监测的难题&#xff0c;阿尔泰科技提供了一套先进的解决方案&#xff0c;利用USB5630-D数据采集卡搭配传感器测试方案&a…

计算机毕业设计 | SpringBoot+vue学生成绩管理系统教务管理系统

1 需求分析 为了满足现代大学教学的需求&#xff0c;本章节将对教务系统平台的选课系统进行深入的需求分析。 1.1 功能模块需求概述 1.1.1 网页功能需求概述 本教务系统平台项目开发了电脑网页端&#xff0c;基于上海大学的教学计划&#xff0c;面向教师和学生两个不同的角…

二、防御保护---防火墙基础知识篇

二、防御保护---防火墙基础知识篇 一、什么是防火墙二、防火墙的发展史1.包过滤防火墙(一个严格的规则表)2.应用代理防火墙(每个应用添加代理)3.状态检测防火墙(首次检查建立会话表)3.入侵检测系统(IDS)-----网络摄像头4.入侵防御系统(IPS)-----抵御2-7层已知威胁5.防病毒网关(…

【解决方案_中国银行滑动拼图验证,只有拖动了滑块之后,才会显示背景框的验证码】

中国银行滑动拼图验证&#xff0c;只有拖动了滑块之后&#xff0c;才会显示背景框的验证码&#xff0c;怎么解决&#xff1f;&#xff1a; 答&#xff1a; 将目标元素显示出来就可以了。 第二条指令&#xff1a;第二条指令里面填&#xff1a;//*[id"dx_captcha_basic_co…

鸿蒙系统扫盲(七):勘误补充总结,收个尾

这是笔者鸿蒙扫盲系列的最后一篇了&#xff0c;准备对过去的六篇扫盲系列文章&#xff0c;错误的地方做一些勘误&#xff0c;并且补充更新一些朋友们感兴趣的知识&#xff0c;最后收个尾。 1.勘误&#xff0c;编译型语言和解释型语言 在鸿蒙系统扫盲&#xff08;五&#xff0…

单片机学习笔记---定时器计数器(含寄存器)工作原理介绍(详解篇2)

目录 T1工作在方式2时 T0工作在方式3时 四种工作方式的总结 定时计数器对输入信号的要求 定时计数器对的编程的一个要求 关于初值计算的问题 4种工作方式的最大定时时间的大小 关于编程方式的问题 实例分析 实例1 实例2 T1工作在方式2时 51单片机&#xff0c;有两个…

全新开源AI代码工具诞生!超越谷歌DeepMind旗下AlphaCode

‍ 听说&#xff0c;谷歌DeepMind开发出的AlphaCode&#xff0c;和上个月刚刚由Gemini推出的AlphaCode 2两位“老大哥”被超越了&#xff1f; 没错&#xff0c;全新开源人工智能代码生成工具AlphaCodium&#xff0c;诞生了&#xff01; 其开发不得不说受到了两位老大哥的启发…

MySQL行格式原理深度解析

MySQL中的行格式&#xff08;Row Format&#xff09;是指存储在数据库表中的数据的物理格式。它决定了数据是如何在磁盘上存储的&#xff0c;以及如何在查询时被读取和解析的。MySQL支持多种行格式&#xff0c;每种格式都有其特定的优点和适用场景。 提升编程效率的利器: 解析…

vector(顺序表)

vector容器就相当于一个顺序表&#xff0c;只不过他把一些功能分装到了容器里 常用接口及语法 构造&#xff1a;vectro<数据类型> 对象名 输出&#xff1a;和顺序表一样我们需要遍历打印&#xff0c;而不能直接用cout打印 对象.push_back () 尾插 对象.pop_back() 尾删…

3款最好用的tron钱包解读:TronLink,Ledger,Bitget钱包

电子钱包是用户连接到区块链网络的重要媒介。除了接收和发送功能外&#xff0c;它还可用于传输虚拟货币。它也是使用分散应用程序&#xff08;DApp&#xff09;的必要工具&#xff01;无论您是想在ON上使用以太坊&#xff0c;EOS还是任何DApp&#xff0c;您都必须先拥有钱包。因…

springboot2.7继承swagger knif4j

maven pom依赖 <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi2-spring-boot-starter</artifactId><version>4.4.0</version></dependency> yml配置 knife4j:enable: trueopenapi:title: …

C++ 入门(二)— 基础知识

文章目录 语句和程序的结构对象和变量变量赋值和初始化cout、cin 和 endl 语句和程序的结构 语句 语句是迄今为止 C 程序中最常见的指令类型。这是因为它们是 C 语言中最小的独立计算单元。在这方面&#xff0c;它们的行为很像自然语言中的句子。 C 中的大多数&#xff08;但…

在Windows11的WSL上运行Llama2-7b-chat 上

最近在玩大模型&#xff0c;玩了ChatGLM3感觉不过瘾&#xff0c;又去玩了Llama2&#xff08;在Windows上简直难的离谱&#xff09;&#xff0c;下边我讲为大家讲一下我的心路历程。 心路历程 第一天&#xff1a;GLM那么简单&#xff0c;有手就行啊&#xff0c;最近Llama2开源…

Android中下载 HAXM 报错 Intel® HAXM installation failed,如何解决?

最近在搭建 Flutter 环境&#xff0c;但是在 Android Studio 中安装 Virtual Device 时&#xff0c;出现了一个 问题 Intel HAXM installation failed. To install Intel HAXM follow the instructions found at: https://github.com/intel/haxm/wiki/Installation-Instructio…

ES6.8.6 Java客户端发起 增删改查 query (bool)、update、delete

文章目录 环境测试数据增单个新增批量新增 删通过delete by api删除通过delete by query api删除删除索引中指定字段&#xff08;script&#xff09; 改单个修改update by api通过_bulk批量修改批量修改update by query api使用script脚本修改 查完全匹配&#xff08;term&…

Linux:进程信号

文章目录 信号的概念实践信号关于前台和后台进程的操作 操作系统与外设信号的产生 前面的篇章结束了信号量的话题&#xff0c;那么接下来引入的是信号的话题&#xff0c;信号和信号量之间没有任何关系&#xff0c;只是名字比较像 信号的概念 在生活中存在各种各样的信号&…