双向链表(详解)

news2024/12/25 13:07:18

在单链表专题中我们提到链表的分类,其中提到了带头双向循环链表,今天小编将详细讲下双向链表。

话不多说,直接上货。

1.双向链表的结构

带头双向循环链表

80f0f03a9fbd4ef6afe363958f5a3f9a.png

注意

这几的“带头”跟前面我们说的“头节点”是两个概念,实际前面的在单链表阶段称呼不严
谨,但是为了更好的理解就直接称为单链表的头节点。
带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这里“放哨 的”。
“哨兵位”存在的意义: 遍历循环链表避免死循环。

插入操作时,不需要检查是否在头部插入,因为哨兵节点作为头结点,总是存在。

删除操作时,不需要处理删除的是否是头节点的情况,因为哨兵节点不会被删除。

简化了代码,因为不需要为头节点和普通节点编写不同的处理逻辑。
双向链表文字上没有什么好说的,具体主要是代码的实现,有了单链表的基础铺垫,双向链表实现也会轻松很多, 主要是理清楚前后节点的关系。

2.双向链表的实现

我们同样是按照项目的格式。

1.头文件的建立(函数库引入,所需函数的导入) 

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//#include <stdbool.h>
typedef int Datatype;
//链表节点创建
typedef struct  ListNode {
	Datatype data;
	struct ListNode* next;
	struct ListNode* prev;
}Node;
//相关函数实现
// 链表初始化,双向链表头节点不能为空
//void LTInit(Node** pphead);
Node* LTInit();
//链表销毁
void LTDestroy(Node* phead);
//链表打印
void LTPrint(Node* phead);
//bool LTEmpty(Node* phead);
//尾插和头插
void LTPushBack(Node* phead, Datatype x);
void LTPushFront(Node* phead, Datatype x);
//尾删和头删
void LTPopBack(Node* phead);
void LTPopFront(Node* phead);
//在pos位置之后插⼊数据
void LTInsert(Node* pos, Datatype x);
//删除指定节点
void LTErase(Node* pos);
//节点找寻,方便指定插入和删除
Node* LTFind(Node* phead, Datatype x);

2.相关函数的实现

2.1 新节点建立

Node* Buynode(Datatype x) {
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL) {
		perror("malloc error");
		exit(1);
	}
	node->data = x;

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

这里为什么节点前后没有指向空指针,因为在后续中链表初始化时,若指向都是空指针,则创建的链表不是双向链表。

在双向链表中,每个节点都有两个指针,一个指向前一个节点(prev),一个指向后一个节点(next)。这样可以实现双向遍历和操作。 当初始化一个双向链表时,需要创建一个头节点(head node),这个头节点不存储实际的数据,只用于标识链表的起始位置。头节点的prev指针和next指针都应该指向自己,这样可以在链表中任意位置插入和删除节点而不需要特殊处理边界情况。

如果初始化时将prev和next指针都指向空,那么在插入和删除节点时就需要特殊处理头节点和尾节点的情况,增加了代码的复杂性。因此,在初始化时将prev和next指针都指向自己是一种简化设计,方便后续操作的方式。

总结来说,prev和next指针不能指向空,是为了简化双向链表的设计和操作。

2.2 链表初始化

Node* LTInit() {
	Node* pphead = Buynode(-1);
	if (pphead == NULL) {
		printf("初始化失败!");
	}
	return pphead;
}

2.3 链表的打印

//链表打印
void LTPrint(Node* phead) {
	Node* pcur = (Node*)malloc(sizeof(Node));
	pcur = phead->next;
	while (pcur!= phead) {
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

2.4 尾插和头插

/尾插和头插
void LTPushBack(Node* phead, Datatype x) {
	assert(phead);
	//Node* ptail = (Node*)malloc(sizeof(Node));
	Node* newnode = Buynode(x);
	newnode->prev = phead->prev;//新节点指向原来的尾节点
	newnode->next = phead;
	phead->prev->next = newnode; //让原本的尾节点指向新的节点
	phead->prev = newnode;
}
void LTPushFront(Node* phead, Datatype x) {
	assert(phead);
	Node* newnode = Buynode(x);
	newnode->next = phead->next;
	newnode->prev = phead;

	phead->next->prev= newnode;//两行不能完全交换,若交换
	phead->next = newnode;//phead->next=newnode;newnode->prev=newnode;

}

2.5 尾删和头删

//尾删和头删
void LTPopBack(Node* phead) {
	//链表必须有效且链表不能为空(只有一个哨兵位)
	assert(phead && phead->next!=phead);
	Node* del = phead->prev;
	Node* ptail = del->prev;
	ptail->next = phead;
	phead->prev = ptail;
	free(del);
	del = NULL;
}

void LTPopFront(Node* phead) {
	assert(phead && phead->next != phead);
	Node* del = phead->next;
	phead->next = del->next;
	del->next->prev = phead;
	free(del);
	del = NULL;
}

2.6 节点的找寻

方便在指定的节点前后进行相关操作

Node* LTFind(Node* phead, Datatype x) {
	Node* find = phead->next;
	while (find!=phead) {
		if (find->data == x)
			return find;
		else
		find = find->next;
	}
	return NULL;
}

2.7 指定节点处的操作

//在pos位置之后插⼊数据
void LTInsert(Node* pos, Datatype x) {
	assert(pos);
	Node* newnode = Buynode(x);
	newnode->next = pos->next;
	newnode->prev = pos;
	pos->next->prev = newnode;
	pos->next = newnode;
}

//删除指定节点
void LTErase(Node* pos) {
	assert(pos);
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);
	pos = NULL;
}

2.8 链表的销毁

void LTDestroy(Node* phead) {
	assert(phead);
	Node* pcur = phead->next;
	while (pcur->next != phead) {
		Node* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(phead);
	phead = NULL;
}

在实现相关函数时,都是从前后节点入手,改变next和prev的指向。

3. 链表的测试

#define _CRT_SECURE_NO_WARNINGS
#include "List.h"
void test1() {
	Node*plist=LTInit();
	//printf("%d", phead->data);
	//头插
	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPrint(plist);
	//尾插
	LTPushBack(plist, 5);
	LTPushBack(plist, 3);
	//LTPrint(plist);
	
	//尾删
	//LTPopBack(plist, 3);
	//LTPopBack(plist, 5);
	//LTPrint(plist);
	//头删
	//LTPopFront(plist, 1);
	//LTPopFront(plist, 2);
	//LTPrint(plist);
	//节点查找
	Node* find = LTFind(plist, 5);
	if (find == NULL)
		printf("Not Found\n");
	else
		printf("找到了\n");
   //指定插入
	LTInsert(find, 66);
	LTPrint(plist);
   //指定节点删除
	LTErase(find);
	LTPrint(plist);
   //链表销毁
	LTDestroy(plist);
	plist = NULL;
}
int main() {
	test1();
	return 0;
}

3.顺序表和链表的优缺点对比(了解)

a1a0210b061f427e879f8a00415db020.png

看完给小编留下点赞,关注加三连吧!!!

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

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

相关文章

【神器来袭】快速解放双手,朋友圈自动转发工具,告别繁琐操作!

朋友圈作为一个重要的营销推广渠道&#xff0c;如果能实现自动转发&#xff0c;那对于很多企业或个人来说&#xff0c;是极好的。下面&#xff0c;就给大家分享一个实用且便捷的朋友圈运营工具——个微管理系统&#xff0c;让大家都能快速推广。 1、多账号登录&#xff0c;定时…

bcrypt.dll文件丢失怎么办?bcrypt.dll怎么修复?

在计算机系统运行过程中&#xff0c;如果发现无法找到或缺失bcrypt.dll文件&#xff0c;可能会引发一系列的问题与故障。首先&#xff0c;由于bcrypt.dll是系统中一个重要的动态链接库文件&#xff0c;它的主要功能可能涉及到系统核心服务、应用程序支持或者特定功能模块的运行…

python爬虫(三)之虎嗅网汽车文章爬虫

python爬虫&#xff08;三&#xff09;之虎嗅网汽车文章爬虫 闲来没事&#xff0c;闲鱼上有个好兄弟要我从虎嗅网上抓一些汽车文章的爬虫&#xff0c;于是大力出奇迹&#xff0c;我写了一个python程序&#xff0c;将这个网站上所有的汽车文章全部抓取下来了&#xff0c;存储到…

2024年记一次Mingw64-13.2.0编译Qt6.6.3,包含文档编译。

My C Development. 前言&#xff1a;不包含qtwebengine。 一、准备文件 &#xff08;1&#xff09;mingw64-13.2.0 下载链接&#xff1a;&#xff0c;ucrt64_13.2_ucrt_posix_rev6_msys2.7z【蓝奏云】。 &#xff08;2&#xff09;qt6.6.3源码 下载链接&#xff1a;Downlo…

电子版图书制作,一键转换可仿真翻页的画册

在数字化浪潮的冲击下&#xff0c;传统纸质图书逐渐被电子版图书取而代之。电子版图书以其便携、环保、更新快速等特点&#xff0c;吸引了越来越多的读者。制作一款既具备电子图书的便捷性&#xff0c;又能仿真翻页的画册&#xff0c;成为当下图书出版行业的新趋势 1.要制作电子…

微信小程序支付(完整版)-ThinkPHP/Uniapp

技术说明 1.前端&#xff1a;uniapp、vue3 2.接口&#xff1a;PHP8、ThinkPHP8、MySQL8.0 3.微信支付- PHP&#xff0c;官方示例文档 4.示例代码的模型及业务自己进行调整&#xff0c;不要一味的复制粘贴&#xff01;&#xff01;&#xff01; 流程说明 1.小程序调用接口…

资源管理游戏模版进入The Sandbox

我们非常高兴地向您介绍 Game Maker 的最新模板&#xff1a;资源管理游戏&#xff01; 这一全新的模板让您能够深入身临其境的游戏体验中&#xff0c;同时掌握令人兴奋的新机制。通过揭开模板的神秘面纱&#xff0c;您可以锤炼您的游戏设计技能。 什么是资源管理游戏&#xff1…

winpcap无法安装提示新版本已经安装-window11解决办法

winpcap无法安装提示新版本已经安装-window11解决办法 问题解决办法 问题 安装ensp的时候跳出来这个问题&#xff0c;说自己的winpcap没安装&#xff0c;建议安装 但当自己去安装一个winpcap的时候&#xff0c;它又跳出来这个&#xff01; WinPcap 4.1.3 Setup A newer versi…

电脑设置在哪里打开?Window与Mac双系统操作指南

随着科技的不断发展&#xff0c;电脑已经成为我们日常生活和工作中不可或缺的一部分。然而&#xff0c;对于许多初学者来说&#xff0c;如何找到并熟悉电脑的设置界面可能是一个挑战。特别是对于那些同时使用Windows和Mac双系统的用户来说&#xff0c;更是需要一篇详尽的指南来…

android进阶-AIDL

参考&#xff1a;Android进阶——AIDL详解_android aidl-CSDN博客 AIDL&#xff08;Android 接口定义语言&#xff09;&#xff0c;可以使用它定义客户端与服务端进程间通信&#xff08;IPC&#xff09;的编程接口&#xff0c;在 Android 中&#xff0c;进程之间无法共享内存&…

latex algorithm2e 库学习总结

案例1 \documentclass{article}\usepackage{xeCJK} \usepackage[]{algorithm2e} %\usepackage{ctex} % 中文包\begin{document}\renewcommand{\algorithmcfname}{算法} % 把标题设置为“算法” \begin{algorithm…

数据库管理-第184期 23ai:干掉MongoDB的不一定是另一个JSON数据库(20240507)

数据库管理184期 2024-05-07 数据库管理-第184期 23ai:干掉MongoDB的不一定是另一个JSON数据库&#xff08;20240507&#xff09;1 JSON需求2 关系型表设计3 JSON关系型二元性视图3 查询视图总结 数据库管理-第184期 23ai:干掉MongoDB的不一定是另一个JSON数据库&#xff08;20…

雪花算法生成全局Id,看这篇就够了

分布式id 雪花算法能够生成一个64位long类型数据&#xff0c;适合做分布式系统的全局标识符&#xff0c;或者分库分表中&#xff0c;同类型数据表的主键 原理探究 雪花算法&#xff1a;以一台服务器为对象&#xff0c;在一毫秒时间内&#xff0c;生成一个自增的long数据特点…

Ps 滤镜:视频

Ps菜单&#xff1a;滤镜/视频 Filter/Video “视频”滤镜子菜单中包含了“NTSC 颜色”和“逐行”两个滤镜。 这两个滤镜都是针对视频和电视播放的特定需求设计的。 “逐行”滤镜主要解决交错视频的视觉问题&#xff0c;而“NTSC 颜色”滤镜则确保色彩在电视播放时的兼容性和准确…

springboot+vue+mysql老年大学会员管理系统+PPT+论文+讲解+售后

现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本老粘大学会员管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&a…

HTML4(二)

文章目录 1 开发者文档2 基本标签2.1 排版标签2.2 语义化标签2.3 行内元素与块级元素2.4 文本标签2.5 常用标签补充 3 图片标签4 超链接标签4.1 跳转页面4.2 跳转文件4.3 跳转锚点4.4 唤起指定应用 5 列表5.1 有序列表5.2 无序列表5.3 自定义列表 6 表格6.1 基本结构6.2 表格标…

如何查看页面对应的Selenium定位参数

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

IO 5.10

在一个进程中&#xff0c;创建一个子线程。 主线程负责&#xff1a;向文件中写入数据 子线程负责&#xff1a;从文件中读取数据 要求使用线程的同步逻辑&#xff0c;保证一定在主线程向文件中写入数据成功之后&#xff0c;子线程才开始运行&#xff0c;去读取文件中的数据#incl…

bean在java中什么意思?这篇文章带你详细了解

bean在java中什么意思&#xff1f;这篇文章带你详细了解 在Java的世界里&#xff0c;你可能会经常听到“Bean”这个词。它听起来像咖啡豆&#xff0c;但实际上与咖啡无关。那么&#xff0c;Java Bean到底是什么呢&#xff1f; 简单来说&#xff0c;Bean是一种特殊的Java类&…

麒麟kylin-v10系统,虚拟机kvm的使用

kvm的使用 虚拟机新建 点击选择对应的iso文件 选择相应的系统 &#xff08;注意&#xff0c;如果这里没有相应的系统比如&#xff1a;windows&#xff0c;可以直接选择Generic default这是通用默认的意思&#xff09; 选择cpu 完成即可 等待安装完毕 网络设置-ssh连接 虚拟…