数据结构之带头双向循环链表
目录
链表的分类
带头双向循环链表的实现
带头双向循环链表的结构
带头双向循环链表的结构示意图
空链表结构示意图
单结点链表结构示意图
多结点链表结构示意图
链表创建结点
双向链表初始化
销毁双向链表
打印双向链表
双向链表尾插
尾插函数测试
双向链表头插
头插函数测试
双向链表尾删
尾删函数测试
双向链表头删
头删函数测试
双向链表查找
双向链表pos位置前插
插入函数测试
双向链表删除pos位置的结点
删除函数测试
利用 ListInsert()函数改造头插尾插函数
尾插函数改造版本
头插函数改造版本
利用ListEarse()函数改造头删 尾删函数
头删函数改造版本
尾删函数改造版本
计算双向链表长度
链表的分类
- 单向/双向
单向列表:每一个结点结构中只保存下一结点的地址,所以很难从后一结点找到前一节点;
双向列表:每一个结点结构中不仅保存下一结点的地址,还保存上一节点的地址;方便寻找前一节点和后一节点;
- 带头/不带头
带头:在头结点之前有一个哨兵位结点,哨兵位的数据域不存储有效数据,指针域指向头结点;
不带头:没有哨兵位结点,尾插尾删考虑头结点情况;
- 循环/非循环
循环:头结点与尾结点相连;
非循环:头结点与尾结点不相连;
上述情况相互组合,共有8种情况, 实际中使用的链表数据结构,都是带头双向循环链表,带头双向循环链表虽然结构复杂,但是其结构具有很多优势,实现反而简单;
带头双向循环链表的实现
带头双向循环链表的结构
typedef int LTDataType;
typedef struct ListNode
{struct ListNode* prev;//前址域-存放前一个结点的地址LTDataType data;//数据域struct ListNode* next;//后址域-存放后一个结点的地址
}ListNode;
逻辑图:
物理图:
带头双向循环链表的结构示意图
-
空链表结构示意图
由图可知,head->prev=head; head->next=head;
-
单结点链表结构示意图
由图可知:
head->next=FirstNode;
head->prev=FirstNode;
FirstNode->prev=head;
FirstNode->next=head;
-
多结点链表结构示意图
由图可知:
head->next=firstnode;
head->prev=tail;
tail->next=head;
firstnode->prev=head;
链表创建结点
//创建链表结点,返回链表结点地址
ListNode* BuyListNode(LTDataType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc failed:");exit(-1);}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}
双向链表初始化
注:函数调用时得到动态开辟的链表空间起始地址的两种方案如下
方案一: 当传参时为链表结点的地址,函数的形参设计为二级指针,只有通过传址调用,可以将动态开辟的链表的起始地址带出函数;
方案二: 设计函数的返回类型为结点指针,返回动态开辟的链表结点指针,如此可以得到链表空间的起始地址;
//初始化链表(空链表)
ListNode* ListInit()
{//创建哨兵位结点ListNode* head = BuyListNode(0);//0不是有效数据//初始化哨兵位结点的指针域head->next = head;head->prev = head;return head;
}
销毁双向链表
- 循环遍历释放结点,包含哨兵位结点;
- 释放前保存下一结点地址,避免地址丢失;
//销毁链表,包含哨兵位结点
void DestoryList(ListNode* phead)
{assert(phead);//创建寻址指针ListNode* cur = phead;//断开循环链表phead->prev->next = NULL;while (cur != NULL){//记录下一结点地址ListNode* next = cur->next;//释放当前结点free(cur);//寻找下一节点cur = next;}return;
}
打印双向链表
- 循环遍历链表打印数据,不显示哨兵位结点的数据域;
- 以哨兵位头结点作为结束标志;
void PrintList(ListNode* phead)
{assert(phead != NULL);ListNode* cur = phead->next;printf("phead<==>");while (cur != phead){printf("%d<==>", cur->data);cur = cur->next;}printf("\n");
}
双向链表尾插
- 尾插先找尾,哨兵位的前址域即为尾结点即tail=head->prev;
- 当链表为空时,连接的逻辑关系相同(创建三个指针变量,按照新结点的前址域指向谁,谁指向新结点,新结点的后址域指向谁,谁指向新结点进行连接);
void ListPushBack(ListNode* phead, LTDataType x)
{assert(phead);//寻找尾结点ListNode* tail = phead->prev;//创建新结点ListNode* newnode = BuyListNode(x);//尾插newnode->prev = tail;tail->next = newnode;newnode->next = phead;phead->prev = newnode;
}
尾插函数测试
void Test1()
{ListNode* plist=ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);PrintList(plist);
}
int main()
{Test1();return 0;
}
运行结果:
双向链表头插
- 头插前先保存哨兵位结点的下一节点即原先真正的首节点;
- 按照按照新结点的前址域指向谁,谁指向新结点,新结点的后址域指向谁,谁指向新结点进行连接从而实现头插,链表为空时,头插逻辑仍然相同;
//链表头插
void ListPushFront(ListNode* phead, LTDataType x)
{assert(phead);//保存原先的首节点ListNode* firstnode = phead->next;//创建新结点ListNode* newnode = BuyListNode(x);//头插newnode->prev = phead;phead->next = newnode;newnode->next = firstnode;firstnode->prev = newnode;
}
头插函数测试
void Test2()
{ListNode* plist = ListInit();ListPushFront(plist, 10);ListPushFront(plist, 20);ListPushFront(plist, 30);ListPushFront(plist, 40);ListPushFront(plist, 50);PrintList(plist);}
int main()
{Test2();return 0;
}
运行结果:
双向链表尾删
- 链表中只剩哨兵位结点,此时链表为空,不再进行尾删;
- 尾删前记录前一节点的地址,方便修改逻辑关系;
//链表尾删
void ListPopBack(ListNode* phead)
{assert(phead);//链表中只剩哨兵位的情况assert(phead->next != phead);//查找尾结点ListNode* tail = phead->prev;//保存尾结点的上一节点ListNode* tailprev = tail->prev;//尾删free(tail);//建立链接关系tailprev->next = phead;phead->prev = tailprev;}
尾删函数测试
void Test3()
{ListNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);PrintList(plist);ListPopBack(plist);PrintList(plist);ListPopBack(plist);PrintList(plist);ListPopBack(plist);PrintList(plist);}
int main()
{Test3();return 0;
}
运行结果:
双向链表头删
- 链表中只剩哨兵位结点,此时链表为空,不再进行头删;
- 头删前记录下一节点的地址,方便修改逻辑关系;
//链表头删
void ListPopFront(ListNode* phead)
{assert(phead);//只剩哨兵位,不再头删assert(phead->next != phead);//保存原先的首节点ListNode* head = phead->next;//保存首结点的下一节点ListNode* headnext = phead->next->next;//头删free(head);//建立链接关系headnext->prev = phead;phead->next = headnext;}
头删函数测试
void Test4()
{ListNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);PrintList(plist);ListPopFront(plist);PrintList(plist);ListPopFront(plist);PrintList(plist);ListPopFront(plist);PrintList(plist);
}
int main()
{Test4();return 0;
}
运行结果:
双向链表查找
- 循环遍历链表,从首节点开始遍历,以哨兵位头结点作为结束标志;
- 根据数据域进行查找,找到返回数据域的结点地址,找不到返回空指针;
ListNode* ListFind(ListNode* phead, LTDataType x)
{assert(phead);//创建遍历指针ListNode* cur = phead->next;//遍历链表while (cur != phead){if ((cur->data) == x){//找到返回下标return cur;}cur = cur->next;}//没找到返回空指针return NULL;
}
双向链表pos位置前插
- 前插时保存pos位置的前一个节点,方便修改逻辑关系;
- 按照按照新结点的前址域指向谁,谁指向新结点,新结点的后址域指向谁,谁指向新结点进行链接;
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos != NULL);//创建新结点ListNode* newnode = BuyListNode(x);//保存pos位置的前一个结点ListNode* posprev = pos->prev;//前插newnode->prev = posprev;posprev->next = newnode;newnode->next = pos;pos->prev = newnode;
}
插入函数测试
void Test5()
{ListNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);int x = 0;printf("请输入查找的数值:");scanf("%d", &x);ListNode* pos = ListFind(plist, x);if (pos == NULL){printf("要查找的值不存在\n");return;}//在查找到数值前插入100ListInsert(pos, 100);PrintList(plist);}
int main()
{Test5();return 0;
}
运行结果:
双向链表删除pos位置的结点
- 链表删除pos位置处的结点前先保存前结点和后结点的地址,方便处理链接关系;
//双向链表删除pos位置
void ListEarse(ListNode* pos)
{assert(pos);//保存pos位置处的前一个和后一个结点;ListNode* posprev = pos->prev;ListNode* posnext = pos->next;//删除pos位置结点free(pos);//建立前后节点的链接关系posprev->next = posnext;posnext->prev = posprev;}
删除函数测试
void Test6()
{ListNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);PrintList(plist);int x = 0;printf("请输入删除的数值:");scanf("%d", &x);ListNode* pos = ListFind(plist, x);if (pos == NULL){printf("要删除的值不存在\n");return;}ListEarse(pos);PrintList(plist);
}
int main()
{Test6();return 0;
}
运行结果:
利用 ListInsert()函数改造头插尾插函数
-
尾插函数改造版本
void Listpushback(ListNode* phead, LTDataType x)
{assert(phead);ListInsert(phead, x);
}
-
头插函数改造版本
void Listpushfront(ListNode* phead, LTDataType x)
{assert(phead);ListInsert(phead->next, x);
}
利用ListEarse()函数改造头删 尾删函数
-
头删函数改造版本
void Listpopfront(ListNode* phead)
{assert(phead);//只剩哨兵位,不再头删assert(phead->next != phead);ListEarse(phead->next);
}
-
尾删函数改造版本
void Listpopback(ListNode* phead)
{assert(phead);//链表中只剩哨兵位的情况assert(phead->next != phead);ListEarse(phead->prev);
}
计算双向链表长度
int ListLength(ListNode* phead)
{assert(phead);int size = 0;ListNode* cur = phead->next;while (cur != phead){++size;cur = cur->next;}return size;
}
相关文章:
数据结构之带头双向循环链表
目录 链表的分类 带头双向循环链表的实现 带头双向循环链表的结构 带头双向循环链表的结构示意图 空链表结构示意图 单结点链表结构示意图 多结点链表结构示意图 链表创建结点 双向链表初始化 销毁双向链表 打印双向链表 双向链表尾插 尾插函数测试 双向链表头插 …...
adb详细教程(四)-使用adb启动应用、关闭应用、清空应用数据、获取设备已安装应用列表
adb对于安卓移动端来说,是个非常重要的调试工具。本篇介绍常用的adb指令 文章目录 一、启动应用:adb shell am start二、使用浏览器打开指定网址:adb shell am start三、杀死应用进程adb shell am force-stop/adb shell am kill四、删除应用所…...
【Spring Boot】日志文件
日志文件 一. 日志文件有什么用二. 日志怎么用三. ⾃定义⽇志打印1. 在程序中得到⽇志对象2. 使⽤⽇志对象打印⽇志3. ⽇志格式说明 四. 日志级别1. ⽇志级别有什么⽤2. ⽇志级别的分类与使⽤ 五. 日志持久化六. 更简单的⽇志输出—lombok1. 添加 lombok 依赖2. 输出⽇志3. lom…...
图像处理与计算机视觉--第五章-图像分割-Canny算子
文章目录 1.边缘检测算子分类2.Canny算子核心理论2.1.Canny算子简单介绍2.2.Canny算子边缘检测指标2.3.Canny算子基本原理 3.Canny算子处理流程3.1.高斯滤波去噪声化3.2.图像梯度搜寻3.3.非极大值抑制处理3.4.双阈值边界处理3.5.边界滞后技术跟踪3.6.Canny算子边缘检测的特点 4…...
LabVIEW开发教学实验室自动化INL和DNL测试系统
LabVIEW开发教学实验室自动化INL和DNL测试系统 如今,几乎所有的测量仪器都是基于微处理器的设备。模拟输入量在进行数字处理之前被转换为数字量。对于参加电气和电子测量课程的学生来说,了解ADC以及如何欣赏其性能至关重要。ADC的不确定性可以根据其传输…...
数据结构: 数组与链表
目录 1 数组 1.1 数组常用操作 1. 初始化数组 2. 访问元素 3. 插入元素 4. 删除元素 5. 遍历数组 6. 查找元素 7. 扩容数组 1.2 数组优点与局限性 1.3 数组典型应用 2 链表 2.1 链表常用操作 1. 初始化链表 2. 插入节点 3. 删除…...
unity 控制玩家物体
创建场景 放上一个plane,放上一个球 sphere,假定我们的球就是我们的玩家,使用控制键w a s d 来控制球也就是玩家移动。增加一个材质,把颜色改成绿色,把材质赋给plane,区分我们增加的白球。 增加组件和脚…...
指数分布优化器(EDO)(含MATLAB代码)
先做一个声明:文章是由我的个人公众号中的推送直接复制粘贴而来,因此对智能优化算法感兴趣的朋友,可关注我的个人公众号:启发式算法讨论。我会不定期在公众号里分享不同的智能优化算法,经典的,或者是近几年…...
Java 时间的加减处理
时间的加减处理 Date date new Date(操作时间(类型Date)-(60000*60*1));600001分钟 60000*60*1 1小时...
基于A4988/DRV8825的四路步进电机驱动器
概述 简化板的CNC sheild V3.0,仅保留步进电机速度与方向的控制引脚STEP/DIR、使能端EN、芯片供电VCC\GND,共计11个引脚。PCB四周开设四个M3通孔,以便于安装固定。此外,将板载的焊死的保险丝更改为可更换的保险座保险丝ÿ…...
万字总结网络原理
目录 一、网络基础 1.1认识IP地址 1.2子网掩码 1.3认识MAC地址 1.4一跳一跳的网络数据传输 1.5总结IP地址和MAC地址 二、网络设备及相关技术 2.1集线器:转发所有端口 2.2交换机:MAC地址转换表+转发对应端口 2.3主机:网络分层从上到下封装 2.4主机&路由器:ARP…...
【AI视野·今日CV 计算机视觉论文速览 第262期】Fri, 6 Oct 2023
AI视野今日CS.CV 计算机视觉论文速览 Fri, 6 Oct 2023 Totally 73 papers 👉上期速览✈更多精彩请移步主页 Daily Computer Vision Papers Improved Baselines with Visual Instruction Tuning Authors Haotian Liu, Chunyuan Li, Yuheng Li, Yong Jae Lee大型多模…...
一文搞懂Jenkins持续集成解决的是什么问题
1、持续集成的定义 大师 Martin Fowler 是这样定义持续集成的: 持续集成是一种软件开发实战, 即团队开发成员经常集成他们的工作. 通常, 每个成员每天至少集成一次, 也就意味着每天可能发生多次集成. 持续集成并不能消除Bug, 而是让它们非常容易发现和改正. 根据对项目实战的理…...
微信小程序去除默认滚动条展示
一、微信小程序改版框架升级后,滚动条默认展示了。 在实际应用中效果不好,如果想默认隐藏掉,代码段如下: /* 去除默认滚动条效果 */ ::-webkit-scrollbar {display:none;width:0;height:0;color:transparent; } 设置成全局样式…...
3.02 创建订单操作详细-订单创建与回滚 (创建订单操作详细)
步骤1: 创建orders订单表,子订单表和订单状态表对应的pojo和mappperOrders和OrderItemsMapperOrderItems和OrderItemsMapperOrderStatus和OrderStatusMapper步骤2:创建OrderService和对应的实现类 public interface OrderService {/*** 用于创建订单相关…...
需求放缓、价格战升级、利润率持续恶化对小鹏汽车造成了严重影响
来源:猛兽财经 作者:猛兽财经 收入和每股收益不及预期,亏损创记录 财报显示,小鹏汽车(XPEV)2023年第二季度收入为50.6亿元人民币(合7亿美元),略低于预期,而且还产生了比预期更大的亏…...
《算法通关之路》chapter19解题技巧和面试技巧
《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。 1 看限制条件 1.1数据规模 有的题目数据规模较小,那么暴力法就可行;如果暴力法不行,那么再稍微加一个诸如缓存和剪枝的优化…...
什么是TF-A项目的长期支持?
安全之安全(security)博客目录导读 问题:Trusted Firmware-A社区每六个月发布一次代码。然而,对于生产中的平台,该策略在维护、重要软件修复的向后兼容性、获得最新的安全缓解措施和整体产品生命周期管理方面不具备可扩展性。 开源软件项目&…...
【LinuxC】时间、时区,相关命令、函数
文章目录 一、序1.1 时间和时区1.11 时间1.12 时区 1.2 查看时间时区的命令1.21 Windows1.22 Linux 二、C语言函数2.1 通用2.11 函数简介2.12 数据类型简介 2.2 windows 和 Linux特有函数2.3 C语言示例 一、序 1.1 时间和时区 1.11 时间 时间是一种用来描述物体运动变化的量…...
mac清理垃圾的软件有哪些?这三款我最推荐
没错,Mac电脑真的好用,但是清理系统垃圾可不是件容易的事。由于Mac系统的封闭性,系统的缓存垃圾常常隐藏得让人发现不了。不过,别担心!有一些专业的Mac清理软件可以帮你解决这一系列问题,让清理垃圾变得轻松…...
复习Day11:链表part04: 206. 反转链表、92. 反转链表II、25. K 个一组翻转链表、148. 排序链表
我用的方法是在leetcode再过一遍例题,明显会的就复制粘贴,之前没写出来就重写,然后从拓展题目中找题目来写。辅以Labuladong的文章看。然后刷题不用CLion了,使用leetcode自带的IDE模拟面试环境。 哈希表章节的题目思路很清晰&…...
一年一度的国庆节又结束了
这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...
雷达干扰和烧穿范围简介
一、干扰信号比 J/S或J-to-S是从目标发射的干扰信号接收的功率(J)与从目标的雷达反向散射接收的功率的比率。 二、烧穿范围 通过电子攻击(J)可以首先检测到目标回波信号(S)的雷达到目标的距离。 三、自保护干扰 也称为主瓣干扰(雷达回波源和干扰机并置)。 烧穿范围…...
“秋天第一只大闸蟹”背后,看见京东一体化供应链
京东似乎正在从一个大闸蟹的物流服务商、销售商,转变为一个大闸蟹的“供货商”。 作者|斗斗 编辑|皮爷 出品|产业家 阳澄湖连续几天的降雨,使得通往蟹塘的路异常难走。 长期驻扎此地的京东相关负责人蹲在蟹塘边的小路上,指着蟹塘说道…...
大模型Java编码能力评估
大模型如火如荼发展,不能只看热闹,也需要躬身入局。要想评估大模型的能力,必须有一个评估方法和评估数据集。下面就梳理下当前大模型是如何评估代码能力的 权威评估 opencompass: https://opencompass.org.cn/datalearner: https://www.dat…...
javascript选择框和选择文本的创建与增加以及设置选中项
<script type"text/javascript">//得到选中项的索引,文本和值函数function getselected(selectedIndex){var selectboxdocument.forms[0].elements["location"];var indexselectbox[selectedIndex];var selectedOptionselectbox.options[…...
汽车驾驶任务的隐马尔可夫模型识别方法研究
汽车驾驶任务的隐马尔可夫模型识别方法研究 一、Introduction 自动驾驶汽车经过了几十年的发展,是目前国内外汽车行业中的重要研究方向。自 动驾驶汽车的智能化需要车辆能够有类“人”的行为,在决策策略上可以满足人的心理 需求。人在驾驶过程中&#…...
Java编程题(完数)
题目 一个正整数的因子是所有可以整除它的正整数。而一个数如果恰好等于除它本身外的因子之和,这个数就称为完数。例如61+2+3(6的因子是1,2,3)。 现在,你要写一个程序,读入两个正整数n和m(1<n<m<…...
国庆day6
国庆day6 汇编语言的组成 伪操作 不参与程序的执行,但是用于告诉编译器程序该怎么编译 如: .text .global .end .if .else .endif .data汇编指令 汇编器将一条汇编指令编译成一条机器码,在内存里一…...
力扣 -- 873. 最长的斐波那契子序列的长度
解题步骤: 参考代码: class Solution { public:int lenLongestFibSubseq(vector<int>& nums) {int nnums.size();unordered_map<int,int> hash;for(int i0;i<n;i){hash[nums[i]]i;}int ret2;vector<vector<int>> dp(n,v…...
做网站的图哪来/网页模板免费html
ubuntu终端获取时间戳:date %s 密钥生成:https://github.com/lhartikk/GenesisH0 要安装X11,选择DarkCoin方法 https://blog.csdn.net/wab719591157/article/details/80747043 修改创世区块中的 pszTimestamp 修改成任意你想输入的话 vAlertPubKey Par…...
网站收录怎么弄/seo外链工具下载
文本和分类标签 为了在Watch app中展示文本,使用标签对象。分类标签支持格式化的文本,可以在运行时被程序修改。 要添加标签到界面控制器,可以把它拖到对应的故事版场景(storyboard),在这里指定标签的初始文…...
重庆做企业网站/广告投放平台都有哪些
方法:1、使用link标签,语法“”;2、使用“import”规则,语法“import url("CSS文件路径");”。本教程操作环境:windows7系统、CSS3&&HTML5版、Dell G3电脑。CSS文件一般叫CSS外部样式表,…...
做系统简单还是网站简单/谷歌关键词优化怎么做
判断输入三条边看是否能构成三角形以及构成的是何种三角形 代码如下: import java.util.Scanner; public class TriangleShape {public static void main(String[] args) {double a, b, c, d 0;System.out.println("此问题是判断输入三条边看是否能构成三角…...
临沂网站开发/网站怎么优化推广
黑客技术点击右侧关注,了解黑客的世界!Java开发进阶点击右侧关注,掌握进阶之路!Python开发点击右侧关注,探讨技术话题!作者丨李坤链接:https://juejin.im/post/5df4d98be51d45584c55322f在掘金上…...
网站备案多少天/韩国比分预测
2019独角兽企业重金招聘Python工程师标准>>> 创建控件数据库的参考: createdb yourdatabase createlang plpgsql yourdatabase psql -d yourdatabase -f postgis.sql psql -d yourdatabase -f postgis_comments.sql psql -d yourdatabase -f spatial_ref…...