当前位置: 首页 > news >正文

双向带头循环链表(图解)

文章目录

  • 头节点(哨兵位)
  • 双向循环结构
    • 头插
    • 尾插
    • 头删
    • 尾删
    • 在指定位置之前插入数据
    • 删除指定位置之前的数据
    • 销毁链表
  • 全部代码
  • 结语

单链表地址

头节点(哨兵位)

什么是头节点呢?头节点也叫哨兵节点,他在链表中进行不了任何操作,只是用来放哨用的,在单链表中我们当我们尾插的时候我们呢需要判断此时的节点是否是空,如果不是空我们才可以尾插,是空的话我们直接赋值,而有了头节点我们就不需要判断是否为空了,直接在头节点后插入即可。这就是头节点的好处,尾插,尾删等也有好处(下文再说)

双向循环结构

单链表中我们只有一个指针指向下一个节点,而双向链表则需要两个指针,一个指向前一个节点,一个指向后一个节点,由于我们需要循环起来,所以最后一个节点我们要指向头节点(这里是指第一个节点) 为了避免搞混我下文把第一个节点称为头节点,哨兵节点就为哨兵节点

哨兵节点如下:
在这里插入图片描述
注意这里我们是要创建一个哨兵节点,所以我们需要返回一个节点,而我们接下来的操作都是在哨兵节点基础上进行的,所以每一次进行基本增删操作的时候可以传一级指针,因为我们接受的哨兵节点肯定是一个指针类型的。

结构定义如下:
在这里插入图片描述

代码实现:

//创建哨兵节点
List* ListHeadCreat()
{List* newhead = (List*)malloc(sizeof(List));if (newhead == NULL){printf("malloc fail!\n");exit(-1);}newhead->val = -1;newhead->next = newhead->prev = newhead;return newhead;
}

头插

首先我们要创建一个新的节点,先让这个新的节点指向自己
头插这里分两种情况:
1. 只有一个哨兵节点
(1)
newNode (新节点)的next指向phead,再让newNodeprev指向phead
(2)
phead->next指向newNode,再让phead->prev指向newNode
2. 不止一个节点
(1)
newNode的next指向phead->prev(此时由于链表的最后一个元素就是哨兵节点上一个节点),再让newNode->prevphead
(2)
phead->prev指向newNode,phead->prev->next(最后一个节点的下一个节点)指向newNode
在这里插入图片描述

代码实现:

//头插
void ListPushFront(List* phead, LDataType x)
{assert(phead);List* newNode = BuyNode(x);	newNode->prev = phead;newNode->next = phead->next;phead->next = newNode;//如果只有一个节点,要把此时的哨兵节点的上一个节点指向新插入的节点if (phead->prev == phead)phead->prev = newNode;
}

在这里插入图片描述

尾插

尾插无论是只有哨兵节点还是多个节点,代码操作都是一样的。
(1)
newNode->next指向phead
newNode->prev
指向最后一个节点
(2)
phead->prev->next(最后一个节点)指向newNode
phead->prev(哨兵节点指向尾)指向newNode

在这里插入图片描述
代码实现:

//尾插
void ListPushBack(List* phead, LDataType x)
{assert(phead);List* newNode = BuyNode(x);newNode->next = phead;newNode->prev = phead->prev;phead->prev->next = newNode;phead->prev = newNode;
}

在这里插入图片描述

头删

头删我们得先保证链表中还有元素,所以我们需要判断以下phead->next是否还是Phead,如果是就说明此时只有哨兵节点

头删也是两种情况都是一样的代码
这里把第一个节点写为del,我们只需要让phead->next指向del,del->prev指向phead,这样就相当于把del从这个链表中断开了,接着就放心的free掉就可以了。

在这里插入图片描述
代码实现:

//头删
void ListPopFront(List* phead)
{assert(phead && phead->next != phead);List* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}

在这里插入图片描述

尾删

尾删跟头删差不多,只不过我们要找到最后一个节点(phead->prev),这就是双链表的好处

尾删的两种情况也都是一致的:

End = phead->prev(最后一个节点)
End->prev->next = phead
phead->next = End->prev
free(End)

在这里插入图片描述
代码实现:

//尾删
void ListPopBack(List* phead)
{assert(phead && phead->prev != phead);List* End = phead->prev;End->prev->next = phead;phead->prev = End->prev;free(End);End = NULL;
}

在这里插入图片描述

在指定位置之前插入数据

如下图:
无论在什么位置之前插入元素,都是一样的代码,首先我们还得写一个查找函数,这里只需要遍历找值就可以了,所以不单独写出来了,跟这里写一块了。
在这里插入图片描述
代码实现:

//查找指定元素
List* ListFind(List* phead, LDataType x)
{assert(phead && phead->next != phead);List* cur = phead->next;while (cur != phead){if (cur->val == x)return cur;cur = cur->next;}return NULL;
}//在指定位置之前插入元素
void ListInsertFront(List* pos, LDataType x)
{assert(pos);List* newNode = BuyNode(x);newNode->prev = pos->prev;newNode->next = pos;pos->prev->next = newNode;pos->prev = newNode;
}

在这里插入图片描述

删除指定位置之前的数据

这里需要注意,由于我们删除的是指定位置之前的数据,所以我们要保证这个pos->prev这个位置不能是哨兵节点,同时pos->prev->prev不能为空,必须有至少两个节点才能删除指定位置之前的数据

在这里插入图片描述
代码实现:

//删除指定位置之前的元素
void ListEarseFront(List* pos, List* phead)
{assert(pos && pos->prev && pos->prev != phead && pos->prev->prev);List* del = pos->prev;pos->prev->prev->next = pos;pos->prev = pos->prev->prev;free(del);del = NULL;
}

在这里插入图片描述

销毁链表

销毁链表分为两种方式:

(1)
由于我们是传入的phead是一级指针,而我们函数的参数也是用的一级指针接受的,因为函数的形参只是实参的一份临时拷贝,由于每次malloc的内存都是在区内开辟的所以我们可以做到释放每一个节点,但是phead这个指针却并不能置为空,如果不置空的话,他指向了被释放掉了的空间,还能找到那个空间,所以此时phead就是野指针,我们也只能在调用**ListDesTory()**这个销毁函数的下面手动把phead置为空
代码实现:

void ListDesTory(List* phead)
{assert(phead);List* cur = phead->next;while (cur != phead){List* next = cur->next;free(cur);cur = next;}free(phead);
}

手动置空
在这里插入图片描述

(2)
当我们想要在函数内部修改变量的时候,我们因该传入它的地址,就像如果我们想要在函数里面修改 int a = 1这个a的值,那我们应该传入的是**(&a),同时我们呢要用int*** 的指针来接收,同样的这里我们呢传入**(&phead),那我们就要用(List**)**来接受,这样在函数内部就可以把phead置为空了

代码实现:

void ListDesTory(List** pphead)
{assert(pphead && *pphead);List* cur = (*pphead)->next;while (cur != (*pphead)){List* next = cur->next;free(cur);cur = next;}free((*pphead));*pphead = NULL;	
}

全部代码

List.h

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int LDataType;
typedef struct List
{struct List* prev;struct List* next;LDataType val;
}List;//创建哨兵节点
List* ListHeadCreat();
//打印节点
void ListPrint(List* phead);
//头插
void ListPushFront(List* phead,LDataType x);
//尾插
void ListPushBack(List* phead, LDataType x);//头删
void ListPopFront(List* phead);
//尾删
void ListPopBack(List* phead);//查找指定元素
List* ListFind(List* phead, LDataType x);
//在指定位置之前插入元素
void ListInsertFront(List* pos,LDataType x);
//删除指定位置之前的元素
void ListEarseFront(List* pos, List* phead);
//销毁链表
void ListDesTory(List* phead);
//销毁链表
//void ListDesTory(List** pphead);

List.c

#define _CRT_SECURE_NO_WARNINGS#include "Lish.h"//创建哨兵节点
List* ListHeadCreat()
{List* newhead = (List*)malloc(sizeof(List));if (newhead == NULL){printf("malloc fail!\n");exit(-1);}newhead->val = -1;newhead->next = newhead->prev = newhead;return newhead;
}//打印节点
void ListPrint(List* phead)
{assert(phead && phead->next);List* cur = phead->next;while (cur != phead){printf("%d ", cur->val);cur = cur->next;}
}List* BuyNode(LDataType x)
{List* newNode = (List*)malloc(sizeof(List));if (newNode == NULL){printf("malloc fail!\n");exit(-1);}newNode->val = x;newNode->next = newNode->prev = newNode;return newNode;
}
//头插
void ListPushFront(List* phead, LDataType x)
{assert(phead);List* newNode = BuyNode(x);	newNode->prev = phead;newNode->next = phead->next;phead->next = newNode;//如果只有一个节点,要把此时的哨兵节点的上一个节点指向新插入的节点if (phead->prev == phead)phead->prev = newNode;
}
//尾插
void ListPushBack(List* phead, LDataType x)
{assert(phead);List* newNode = BuyNode(x);newNode->next = phead;newNode->prev = phead->prev;phead->prev->next = newNode;phead->prev = newNode;
}//头删
void ListPopFront(List* phead)
{assert(phead && phead->next != phead);List* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}
//尾删
void ListPopBack(List* phead)
{assert(phead && phead->prev != phead);List* End = phead->prev;End->prev->next = phead;phead->prev = End->prev;free(End);End = NULL;
}//查找指定元素
List* ListFind(List* phead, LDataType x)
{assert(phead && phead->next != phead);List* cur = phead->next;while (cur != phead){if (cur->val == x)return cur;cur = cur->next;}return NULL;
}//在指定位置之前插入元素
void ListInsertFront(List* pos, LDataType x)
{assert(pos);List* newNode = BuyNode(x);newNode->prev = pos->prev;newNode->next = pos;pos->prev->next = newNode;pos->prev = newNode;
}//删除指定位置之前的元素
void ListEarseFront(List* pos, List* phead)
{assert(pos && pos->prev && pos->prev != phead && pos->prev->prev);List* del = pos->prev;pos->prev->prev->next = pos;pos->prev = pos->prev->prev;free(del);del = NULL;
}
//销毁链表
void ListDesTory(List* phead)
{assert(phead);List* cur = phead->next;while (cur != phead){List* next = cur->next;free(cur);cur = next;}free(phead);
}
销毁链表
//void ListDesTory(List** pphead)
//{
//	assert(pphead && *pphead);
//	List* cur = (*pphead)->next;
//	while (cur != (*pphead))
//	{
//		List* next = cur->next;
//		free(cur);
//		cur = next;
//	}
//	free((*pphead));
//	*pphead = NULL;	
//}

结语

OK了,链表在单链表和双向循环带头链表写完了就告一段落了,感觉有帮助的话可以点点赞,如果有哪写错了欢迎指出来~
在这里插入图片描述

相关文章:

双向带头循环链表(图解)

文章目录 头节点(哨兵位)双向循环结构头插尾插头删尾删在指定位置之前插入数据删除指定位置之前的数据销毁链表 全部代码结语 单链表地址 头节点(哨兵位) 什么是头节点呢?头节点也叫哨兵节点,他在链表中进行不了任何操作,只是用来放哨用的,在单链表中我们当我们尾插的时候我们…...

富文本编辑器 iOS

https://gitee.com/klkxxy/WGEditor-mobile#wgeditor-mobile 采用iOS系统浏览器做的一款富文本编辑器工具。 原理就是使用WKWebView加载一个本地的一个html文件&#xff0c;从而达到编辑器功能的效果&#xff01; 由于浏览器的一些特性等&#xff0c;富文本编辑器手机端很难做…...

【OceanBase诊断调优】—— checksum error ret=-4103 问题排查

适用版本 OceanBase 数据库所有版本。 什么是 checksum data checksum&#xff1a;一个 SSTable 中所有宏块内存二进制计算出来的 checksum 值。反映了宏块中的数据和数据分布情况。如果宏块中数据一致但是数据分布不一致&#xff0c;计算出来的 checksum 也不相等。 column…...

融合Transformer与CNN,实现各任务性能巅峰,可训练参数减少80%

论文er看过来&#xff0c;今天给各位推荐一个热门创新方向&#xff1a;CNNTransformer。 众所周知&#xff0c;CNN通过多层卷积自动学习空间层级特征&#xff0c;能够有效提取图像局部特征。而Transformer通过自注意力机制全局建模&#xff0c;能够有效处理长距离依赖关系。 …...

K8s 多租户管理

一、K8s 多租户管理 多租户是指在同一集群中隔离多个用户或团队&#xff0c;以避免他们之间的资源冲突和误操作。在K8s中&#xff0c;多租户管理的核心目标是在保证安全性的同时&#xff0c;提高资源利用率和运营效率。 在K8s中&#xff0c;该操作可以通过命名空间&#xff0…...

Java面试题:Synchronized和Lock的对比

Synchronized和Lock对比 语法层面 Synchronized是关键字,源码在jvm中,用c语言实现 使用时,退出同步代码块时会自动释放 Lock是接口,源码由jdk提供,用java语言实现 使用时,需要手动调用unlock方法进行释放 功能层面 都属于悲观锁,具备基本的互斥,同步,锁重入功能 但Lock…...

VPN方案和特点

VPN方案和特点 VPN&#xff0c;或者称为虚拟专用网络&#xff0c;是一种保护你的在线安全和隐私的技术。它可以创建一个加密的连接&#xff0c;使你的在线活动对其他人不可见。以下是一些常见的VPN协议和它们的特点&#xff1a; 开放VPN (OpenVPN)&#xff1a;这是一种极为可…...

力扣HOT100 - 84. 柱状图中最大的矩形

解题思路&#xff1a; 单调栈 对于一个高度height[ i ]&#xff0c;找左右两边均严格小于它的值。 class Solution {public int largestRectangleArea(int[] heights) {int n heights.length;int[] left new int[n];int[] right new int[n];Deque<Integer> mono_st…...

【吃透Java手写】3-SpringBoot-简易版-源码解析

【吃透Java手写】SpringBoot-简易版-源码解析 1 SpringbootDemo2 准备工作2.1 Springboot-my2.1.1 依赖2.1.2 SpringBootApplication2.1.3 SJBSpringApplication2.1.3.1 run方法 2.2 Springboot-user2.2.1 依赖2.2.2 UserController2.2.3 UserApplication 2.3 分析run方法的逻辑…...

maven mirrorOf的作用

在工作中遇到了一个问题导致依赖下载不了&#xff0c;最后发现是mirror的问题&#xff0c;决定好好去看一下mirror的配置&#xff0c;以及mirrorOf的作用&#xff0c;以前都是直接复制过来使用&#xff0c;看了之后才明白什么意思。 过程 如果你设置了镜像&#xff0c;镜像会匹…...

Centos7 安装 MySQL5.7 使用 RPM 方式

1 访问网站 https://downloads.mysql.com/archives/community/ 选择合适的版本&#xff0c;点击 Download。 2 上传下载好的 mysql-5.7.44-1.el7.x86_64.rpm-bundle.tar 文件到 Centos7 机器&#xff0c;这里放到了 下载 目录。 3 解压 mysql-5.7.44-1.el7.x86_64.rpm-bundle.…...

代码随想录算法训练营day21 | 513.找树左下角的值、112. 路径总和、106.从中序与后序遍历序列构造二叉树

513.找树左下角的值 迭代法比较简单&#xff0c;层序遍历&#xff0c;找到最下面一层的第一个节点。题目已经说明节点数>1了 class Solution:def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:queue collections.deque()queue.append(root)result ro…...

微信小程序知识点归纳(一)

前言&#xff1a;适用于有一定基础的前端开发同学&#xff0c;完成从网页开发到小程序开发的知识转换。 先立框架&#xff0c;后砌墙壁 回顾&#xff1a;了解微信小程序开发流程-CSDN博客 初始页面结构&#xff0c;三部分pages、utils、配置&#xff0c;分别存放页面、工具类…...

wangEditor富文本编辑器与layui图片上传

记录&#xff1a;js 显示默认的wangEditor富文本编辑器内容和图片 <style>body {background-color: #ffffff;}.layui-form-select dl{z-index:100000;} </style> <div class"layui-form layuimini-form"><div class"layui-form-item"…...

爬虫学习:XPath提取网页数据

目录 一、安装XPath 二、XPath的基础语法 1.选取节点 三、使用XPath匹配数据 1.浏览器审查元素 2.具体实例 四、总结 一、安装XPath 控制台输入指令&#xff1a;pip install lxml 二、XPath的基础语法 XPath是一种在XML文档中查找信息的语言&#xff0c;可以使用它在HTM…...

【雅思写作】Vince9120雅思小作文笔记——P1 Intro(前言)

文章目录 链接P1 Intro&#xff08;前言&#xff09;字数限制题型综述&#xff08;problem types overview&#xff09;1. **柱状图&#xff08;Bar Chart&#xff09;** - 描述不同类别在某个或多个变量上的数据量比较。2. **线图&#xff08;Line Graph&#xff09;** - 展示…...

【面试干货】HTTPS 工作原理

【面试干货】HTTPS 工作原理 1、握手阶段&#xff08;Handshake&#xff09;2、密钥协商阶段3、加密通信阶段4、结束通信阶段 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff…...

Cocos Creator 中编码规范 (6)

Cocos中命名规范 创建文件夹&#xff0c;全小写。创建脚本&#xff0c;首字母大写的驼峰形式。创建变量&#xff0c;首字母小写的驼峰形式 官方的编码规范...

Vue3:menu导航栏出现多个同一跳转路径的菜单处理

文章目录 需求整理实现思路实现过程 需求整理&#xff0c;实现思路 最近公司想将之前老的项目整理出来&#xff0c;因为这个老项目内容太杂什么页面都往里面塞&#xff0c;导致菜单特别多&#xff0c;公司就像将这个老的项目迁出来&#xff0c;这个旧的项目本来是后端PHP写的。…...

SAM轻量化应用Auto-SAM、Group-Mix SAM、RAP-SAM、STLM

1. Auto SAM&#xff08;Auto-Prompting SAM for Mobile Friendly 3D Medical Image Segmentation&#xff09; 1.1 面临问题 医学背景&#xff1a; &#xff08;1&#xff09;与自然图像相比&#xff0c;医学图像的尺寸更小&#xff0c;形状不规则&#xff0c;对比度更低。&…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景

Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知&#xff0c;帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量&#xff0c;能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度&#xff0c;还为机器人、医疗设备和制造业的智…...

链式法则中 复合函数的推导路径 多变量“信息传递路径”

非常好&#xff0c;我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题&#xff0c;统一使用 二重复合函数&#xff1a; z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y))​ 来全面说明。我们会展示其全微分形式&#xff08;偏导…...

HTTPS证书一年多少钱?

HTTPS证书作为保障网站数据传输安全的重要工具&#xff0c;成为众多网站运营者的必备选择。然而&#xff0c;面对市场上种类繁多的HTTPS证书&#xff0c;其一年费用究竟是多少&#xff0c;又受哪些因素影响呢&#xff1f; 首先&#xff0c;HTTPS证书通常在PinTrust这样的专业平…...

持续交付的进化:从DevOps到AI驱动的IT新动能

文章目录 一、持续交付的本质&#xff1a;从手动到自动的交付飞跃关键特性案例&#xff1a;电商平台的高效部署 二、持续交付的演进&#xff1a;从CI到AI驱动的未来发展历程 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/101f72defaf3493ba0ba376bf09367a2.png)中国…...

Gitlab + Jenkins 实现 CICD

CICD 是持续集成&#xff08;Continuous Integration, CI&#xff09;和持续交付/部署&#xff08;Continuous Delivery/Deployment, CD&#xff09;的缩写&#xff0c;是现代软件开发中的一种自动化流程实践。下面介绍 Web 项目如何在代码提交到 Gitlab 后&#xff0c;自动发布…...

可下载旧版app屏蔽更新的app市场

软件介绍 手机用久了&#xff0c;app越来越臃肿&#xff0c;老手机卡顿成常态。这里给大家推荐个改善老手机使用体验的方法&#xff0c;还能帮我们卸载不需要的app。 手机现状 如今的app不断更新&#xff0c;看似在优化&#xff0c;实则内存占用越来越大&#xff0c;对手机性…...