双向链表-

news2024/9/22 21:57:14

链表特性:带头/不带头

                  循环/非循环

--->排列组合后,共有8种链表结构

一.双向链表的定义

前一个节点存了后一个节点的地址,后一个节点也存了前一个节点的地址,即循环链表

二.代码解析

//双向链表
//与非循环链表区别!该双向链表事先设置了一个哨兵位节点,所以不用传入二级指针,即可改变链表里面的内容
#include <iostream>
#include <stdlib.h>
#include <assert.h>
using namespace std;
//1.定义链表结构
typedef int LTDateType;//适用于多类型(eg:int)
typedef struct ListNode
{
	LTDateType data;//链表中储存的数据
    struct ListNode* prev;//指向节点的前一个节点
	struct ListNode* next;//指向节点的后一个节点
}LTNode;
//2.初始化链表
//法1,传入二级指针,同非循环链表,即void ListInit(LTNode**pphead);
//法2,设置哨兵位头节点
LTNode* ListInit()
{
	//设置哨兵位头节点
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	//循环链表
	phead->next = phead;
	phead->prev = phead;
	return phead;
}
//3.打印链表
void ListPrint(LTNode* phead)
{
	//循环链表,从head的下一个节点开始打印
	assert(phead);//链表不能为空
	//head是哨兵位节点,是空的,所以不打印
	LTNode* cur = phead->next;
	while (cur!=phead)
	{
		cout << cur->data<<"->";
		cur = cur->next;
	}
	cout << endl;
}
//4.尾插
//已经创建了一个哨兵位头节点,所以尾插不需要传入二级指针
void ListPushBack(LTNode* phead,LTDateType x)
{
	//断言,链表不为空
	assert(phead);
	//创建一个新的节点
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	newnode->data=x;//在新创建的节点中插入数据
	//LTNode* newnode=BuyListNode(x);
	LTNode* tail = phead->prev;//定义一个变量指向链表的尾部
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}
//5.尾删
void ListPopBack(LTNode* phead)
{
	//断言,链表不为空
	assert(phead);
	//防止删除掉创建的哨兵位节点
	assert(phead->next != phead);
	//法1
	//LTNode* tail = phead->prev;//指向链表的尾节点
	//tail->prev->next = phead;
	//phead->next = tail->prev;
	//free(tail);
	//法2,多定义几个变量
	LTNode* tail = phead->prev;//尾节点
	LTNode* tailPrev = tail->prev;//尾节点的前一个节点
	free(tail);
	tailPrev->next = phead;
	phead->prev = tailPrev;
}
LTNode* BuyListNode(LTDateType x)//用于创建新的节点
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	newnode->data = x;
	newnode->next = NULL;//滞空
	newnode->prev = NULL;//滞空
	return newnode;
}
//6.头插
void ListPushFront(LTNode* phead, LTDateType x)
{
	//注意插入的位置!
	//在哨兵位节点和第一个位置之间插入数据
	assert(phead);
	//由于每次都要检查扩展一个新的节点,所以设置一个函数专门用于扩展
	LTNode* newnode = BuyListNode(x);
	//创建好节点后,开始进行插入操作
	//找到哨兵位头节点后真正的第一个节点
	LTNode* next = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = next;
	next->prev = newnode;
}
//7.头删
void ListPopFront(ListNode* phead)
{
	assert(phead);
	//防止将哨兵位节点删掉
	assert(phead->next != phead);
	//定义多个变量,增加程序的可读性
	LTNode* next = phead->next;//要删除的头部节点
	LTNode* nextNext = next->next;//头数据的下一个节点
	phead->next = nextNext;
	nextNext->prev = phead;
	//要记得将删除的头部节点的内存还给系统
	free(next);
}
//8.查找
//根据传进来的数字在链表中查找是否有相同的
LTNode* ListFind(LTNode* phead,LTDateType x)
{
	assert(phead);
	//从哨兵位节点的下一个节点开始查找
	LTNode* cur = phead->next;
	//限定范围,即何时停止查找
	while (cur!= phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	//未查找到
	return NULL;
}
//9.pos位置前插入
void ListInsert(LTNode* pos,LTDateType x)//可直接代替头插和尾差,即直接在这两个函数体中调用该函数
{
	assert(pos);
	//创建一个新节点
	LTNode* newnode = BuyListNode(x);
	//创建一个变量指向pos位置前的一个节点
	LTNode* posPrev = pos->prev;
	posPrev->next = newnode;
	newnode->prev = posPrev;
	newnode->next = pos;
	pos->prev = newnode;
}
//10.删除pos位置
void ListErase(LTNode* pos)
{
	assert(pos);
	LTNode* posPrev = pos->prev;
	LTNode* posNext = pos->next;
	posPrev->next = posNext;
	posNext->prev = posPrev;
	free(pos);
	pos = NULL;
}
//11.销毁链表
void ListDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	//先释放除哨兵位头结点以外的节点,再单独销毁哨兵位头节点
	while (cur != phead)
	{
		LTNode* next = cur->next;//先保存下一个节点的地址,防止被置成随机值后找不到
		free(cur);
		cur = next;
	}
	free(phead);
	phead = NULL;//不能使plist清理干净,可在测试类中手动清理
}

每写一个功能,都要进行一次测试,以下为所有的测试类合集:

//测试类
void Test()
{
	//初始化
	LTNode* plist = ListInit();
	//尾插
	cout << "尾插测试:" << endl;
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 5);
	ListPrint(plist);
	cout << "---------------" << endl;
	//尾删
	cout << "尾删测试:" << endl;
	ListPopBack(plist);
	ListPopBack(plist);
	ListPrint(plist);
	cout << "---------------" << endl;
	cout << "头插测试:" << endl;
	ListPushFront(plist, 3);
	ListPushFront(plist,4);
	ListPushFront(plist, 5);
	ListPrint(plist);
	cout << "---------------" << endl;
	cout << "头删测试:" << endl;
	ListPopFront(plist);
	ListPrint(plist);
	cout << "---------------" << endl;
	cout << "查找测试:(修改数值)" << endl;
	LTNode* pos = ListFind(plist,4);
	//利用查找修改数值
	if (pos)
	{
		pos->data = 7;
	}
	ListPrint(plist);
	cout << "---------------" << endl;
	cout << "在pos位置前插入测试:" << endl;
	//配合查找,插入数据
	LTNode* search = ListFind(plist, 1);
	//即在1的前面插入节点
	ListInsert(search, 9);
	ListPrint(plist);
	cout << "---------------" << endl;
	cout << "删除pos位置节点测试:" << endl;
	LTNode* search2 = ListFind(plist, 2);
	ListErase(search2);
	ListPrint(plist);
	cout << "---------------" << endl;
	//销毁链表
	ListDestroy(plist);
	plist = NULL;
	ListPrint(plist);
}
int main()
{
	Test();
	return 0;
}

以下为运行测试类时的最终结果:(因最终链表销毁,故无法进行打印,assert自动截止)

三.顺序表&链表

CPU

L1cache

L2cache                 寄存器                                      分别遍历顺序表和链表

L3cache

(假设顺序表的地址为0x00123400)访问储存位置时,先看这个地址在不在缓冲区中,在就直接访问,不再就先加载到缓存,再访问。假设不命中,依次加载20byte到缓存(具体加载多大取决于硬件体系)

顺序表是连续的,故CPU高速缓存命中率高

链表是非连续的,每个节点都由一个指针,故CPU高速缓存命中率更低

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

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

相关文章

基于SpringBoot+Vue+MySQL的医院信息管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 在当今社会&#xff0c;随着医疗服务需求的不断增长和医疗信息化的快速发展&#xff0c;提升医院管理效率和服务质量成为了医疗行业的核心需求。传统的医院管理模式面临着效率低下、资源分配不均、患者就医体验差等问题。为了应…

react hooks--useReducer

概述 很多人看到useReducer的第一反应应该是redux的某个替代品&#xff0c;其实并不是 ◼ useReducer仅仅是useState的一种替代方案&#xff1a;  在某些场景下&#xff0c;如果state的处理逻辑比较复杂&#xff0c;我们可以通过useReducer来对其进行拆分&#xff1b; 或…

分布式事务详细笔记:什么是分布式事务--Seata--XA模式--AT模式

目录 1.分布式事务 1.1.什么是分布式事务 1.2.认识Seata 1.3.部署TC服务 1.3.1.准备数据库表 1.3.2.准备配置文件 1.3.3.Docker部署 1.4.微服务集成Seata 1.4.1.引入依赖 1.4.2.改造配置 1.4.3.添加数据库表 1.5.XA模式 1.5.1.两阶段提交 1.5.2.Seata的XA模型 1…

【C++】C++入门概念(二)

引用 概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。 比如&#xff1a;李逵&#xff0c;在家称为"铁牛"&#xff0c;江湖上人称"黑旋…

C++从入门到起飞之——多态 全方位剖析!

&#x1f308;个人主页&#xff1a;秋风起&#xff0c;再归来~&#x1f525;系列专栏&#xff1a;C从入门到起飞 &#x1f516;克心守己&#xff0c;律己则安 目录 1. 多态的概念 2. 多态的定义及实现 2.1 多态的构成条件 2.1.1 实现多态还有两个必须重要条件&…

数据结构-树和二叉树

树 和 二叉树 1.树的概念 树 tree 是n(n>0)个节点的有限集 在任意的一个非空树中 (1)有且仅有一个特定的被称为 根(root) 的节点 (2)当n>1时, 其余的节点可分为m(m>0)个互不相交的有限集T1, T2, T3, .... …

Java 入门指南:JVM(Java虚拟机)—— Java 类加载器详解

类加载器 类加载器&#xff08;Class Loader&#xff09;是 Java 虚拟机&#xff08;JVM&#xff09;的一部分&#xff0c;它的作用是将类的字节码文件&#xff08;.class 文件&#xff09;从磁盘或其他来源加载到 JVM 中。类加载器负责查找和加载类的字节码文件&#xff0c;并…

HTML和CSS做一个无脚本的手风琴页面(保姆级)

一、前言 使用HTML和CSS做一个无脚本的手风琴页面。让知识以自己喜欢的方式进入脑子&#xff0c;适用于很多场景&#xff0c;比如以下&#xff1a; 【注&#xff1a;图片源自百度】 二、HTML框架 使用外部样式表&#xff0c;将CSS文件用link标签引入 整体框架如下图&#x…

基于微信小程序的游泳馆管理系统--论文源码调试讲解

2 关键技术介绍 2.1 SSM框架 开发信息管理系统的主流框架是SSM&#xff08;Spring Spring MVC MyBatis&#xff09;&#xff0c;SSM框架web层使用Spring MVC框架&#xff0c;使传输前后端数据变得简单&#xff1b;对于业务层使用Spring作为轻量级控制反转和面向切面的容器框…

Leetcode面试经典150题-39.组合总数

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同一个 数字可以 无限制重复被选取 。如…

基于Ambari搭建hadoop生态圈+Centos7安装教程

当我们学习玩搭建hadoop的时候&#xff0c;未免也会遇见很多繁琐的事情&#xff0c;比如很多错误&#xff0c;需要解决。在以后公司&#xff0c;也不可能让你一个一个搭建hadoop&#xff0c;成千上万的电脑&#xff0c;你在一个个搭建&#xff0c;一个个报错&#xff0c;而且每…

深度学习与应用:人体关键点检测

实验二 深度学习与应用&#xff1a;人体关键点检测 1、 实验目的 了解人体关键点检测基础流程熟悉YOLOV7-pose模型结构掌握 YOLOv7-pose 模型的训练、Fine-tuning 以及推理的能力掌握YOLOV7-pose模型对实际问题的应用能力&#xff0c;了解如何在特定的场景和任务中应用该模型…

为什么git有些commit记录,只有git reflog可以看到,git log看不到?

文章目录 原因分析1. git log 只能显示 **可达的** 提交2. git reflog 记录所有引用的变更 常见导致 git log 看不到提交的原因1. git reset 操作2. git rebase 操作3. 分支删除4. git commit --amend5. 垃圾回收&#xff08;GC&#xff09;* 如何恢复 git log 看不到的提交&am…

QT实现升级进度条页面

一.功能说明 在Qt中实现固件升级的进度条显示窗口&#xff0c;你可以通过创建一个自定义的对话框&#xff08;Dialog&#xff09;来完成。这个对话框可以包含一个进度条&#xff08;QProgressBar&#xff09;、一些文本标签&#xff08;QLabel&#xff09;用于显示状态信息&am…

学生管理系统1.0版本

学生管理系统1.0版本有5个功能&#xff0c;即添加学生、删除学生、修改学生、查看全部学生、退出系统。 里面对添加重复学号、删除和修改不存在的学号等问题都有相应的解决办法。 代码区&#xff1a; Student.java package student;//快捷键Altinsert public class Student …

iOS 消息机制详解

应用 解决NSTimer、CADisplayLink循环引用。 二者都是基于runloop的定时器&#xff0c;由于处理事件内容不一样&#xff0c;runloop 每运行一次运行耗时就不一样&#xff0c;无法准确的定时触发timer的事件。 NSProxy 与 NSObject 如果继承自NSProxy 直接开始消息转发&…

【comfyUI工作流】一键生成专属欧美漫画!

现在你不需要在webui上手动设置一堆的参数 来将自己的照片转绘成欧美漫画插画 可以通过我制作的工作流一键完成转绘&#xff0c;更加效率便捷&#xff0c; 而且不需要你懂什么专业的AI绘画知识&#xff0c;会打开工作流&#xff0c;上传图片就可以 工作流特点 真实照片一键…

linux StarRocks 安装

一、检查服务器是否支持avx2&#xff0c;如果执行命令显示空&#xff0c;则不支持&#xff0c;那么安装后无法启动BE cat /proc/cpuinfo |grep avx2我的支持显示如下&#xff1a; 二、安装 docker run -p 9030:9030 -p 8030:8030 -p 8040:8040 -p 9001:9000 --privilegedtrue…

开源PHP导航网源码/精美简约网址导航收录网站/QQ技术导航程序

源码简介&#xff1a; 一款给力的开源PHP导航网源码&#xff0c;它不仅外观精美简约&#xff0c;还是个网址导航收录网站/QQ技术导航程序哦&#xff01; 在信息爆炸的时代&#xff0c;找网页就像大海捞针一样难。但是有了像PHP 导航网这样的神器&#xff0c;一切都变得简单了…

Gin框架入门(1)--路由搭建与Json处理

背景知识 为什么要使用Go框架 如果不使用框架&#xff0c;在创建服务器和调用端口时会遇到各种各样“奇怪”的问题&#xff08;就是出错的排查方向可能达到十几种&#xff09;&#xff0c;而且这些问题很难有相似性。同时作为适应于微服务的一门语言&#xff0c;代码的规范化…