带头双向循环链表及链表总结
1、链表种类大全

1、链表严格来说可能用2*2*2=8种结构,从是否带头,是否循环,是否双向三个角度区分。
2、无头单向循环链表一般不会在实际运用中直接存储数据,而会作为某些更复杂结构的一个子结构,毕竟它只在头插、头删时具有效率上的优势。
3、带哨兵卫的头有利于解决尾插时多种讨论的复杂情况。
双向有利于insert、erase的实现,这两个函数涉及到对pos位置结点的前一个结点的操作,而双向链表由于存放了prev指针,可以轻松找到前一个结点(不用为了找前一个结点而再次遍历),从而完成相关功能。
循环有利于直接通过phead->prev找到tail,不用遍历,提高尾部操作的效率。
2、接口函数
//打印
void LTPrint(LTNode* phead);
//初始化
LTNode* LTInit();
//销毁
void LTDestroy(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead,LTDataType x);
//尾删
void LTPopBack(LTNode* phead);
//头插
void LTPustFront(LTNode* phead,LTDataType x);
//头删
void LTPopFront(LTNode* phead);
//查找链表中某个值的位置
LTNode* LTFind(LTNode* phead,LTDataType x);
//pos位置之前插入
void LTInsert(LTNode* pos, LTDataType x);
//pos位置删除
void LTErase(LTNode* pos);
结点的创建
typedef int LTDataType;typedef struct LTNode
{struct LTNode* prev;struct LTNode* next;LTDataType data;}LTNode;
与之前相同,以int类型作为链表存储的数据类型。
3、初始化
在之前的无头单向非循环链表中,我们用一个plist指针指向链表,创建时只需要将plist置空即可,操作简单,不需要通过函数实现。
在顺序表的实现中,要考虑到sz(顺序表当前存储的数据个数),capacity(顺序表当前容量),还有一个指向顺序表的初始指针(避免使用数组导致的一些缺点),过程较复杂需要通过函数。
在带头双向循环链表中,由于需要创建一个哨兵卫的头结点,且为循环结构,next,prev都指向tail,无元素时tail为自己,可以通过init函数来实现。 
//初始化
LTNode* LTInit()
{//初始化时创建一个哨兵卫头结点LTNode* phead = (LTNode*)malloc(sizeof(LTNode));if (NULL == phead){perror("malloc fail");return NULL;}phead->next = phead->prev = phead;phead->data = -1;return phead;}
phead的data暂且设置一个-1,其它值也可以。
4、打印
//打印
void LTPrint(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;printf("<head>");while (cur != phead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");
}
phead一定不为NULL,因此可以assert(phead)防止使用者误传入NULL。
phead头结点不为实际值,因此从phead->next开始遍历,cur只要不为phead的都要进入循环完成打印。
5、尾部操作
1、创建新结点
LTNode* BuyLTNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (NULL == newnode){perror("malloc fail");return NULL;}newnode->data = x;newnode->next = newnode->prev = NULL;return newnode;}
插入操作前,需要malloc一个新结点。为防止野指针,在创建过程中就将其next和prev置为空
2、判断链表是否为空
删除操作前,需要判断链表是否为空。
//判断链表原来是否为空的函数
bool IsEmpty(LTNode* phead)
{return phead->next == phead;
}
这里采用bool值,返回值为true(非0),和false(0)两种,需要包含stdbool.h头文件,这里的phead->next如果等于phead,即链表中只有哨兵卫头结点,即为空,返回为1(非0),代表链表的状态为空
3、尾插
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = BuyLTNode(x);if (NULL == newnode){perror("BuyLTNode fail");return;}//按照之前的思路,尾插需要先找尾// 这里phead的prev直接找尾 需要4步指针操作LTNode* tail = phead->prev;tail->next = newnode;newnode->prev = tail;phead->prev = newnode;newnode->next = phead;}
先利用phead->prev找到原来的尾结点tail,然后创建新的尾结点,随后通过4步指针操作建立newnode与tail和phead的联系。(不用遍历找尾,提高了效率)


带哨兵卫的头结点的优势,不用讨论原链表为空的情况。
4、尾删
//尾删
void LTPopBack(LTNode* phead)
{assert(phead);//先判断原来是否为空assert(!IsEmpty(phead));LTNode* tail = phead->prev;LTNode* prev = tail->prev;free(tail);tail = NULL;phead->prev = prev;prev->next = phead; }
assert内为!IsEmpty,即为空时报错。
分别用tail和prev标记要删除的尾结点以及前一个结点,free掉tail,利用两个指针建立prev与phead的联系,使其成为新尾。

多删除时assert报错
6、头部操作
1、头插
//头插
void LTPustFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = BuyLTNode(x);if (NULL == newnode){perror("BuyLTNode fail");return;}newnode->next = phead->next;phead->next->prev = newnode;newnode->prev = phead;phead->next = newnode;}
通过4个指针建立newnode与phead、phead->next的联系,要注意顺序,先建立与phead->next
的,否则其会被改变无法找到。
当然,在开始时新建立一个next指针指向phead->next的结点也可以。

2、头删
//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(!IsEmpty(phead));LTNode* first = phead->next;phead->next = first->next;first->next->prev = phead;free(first);first = NULL;}
先利用first标记要删除的头结点,然后用两个指针建立phead与first的next结点之间的关系,最后free掉first。

头部操作本身就是链表的优势,因此仍不需要遍历
7、查找及指定位置pos操作
1、查找函数
//查找链表中某个值的位置
LTNode* LTFind(LTNode* phead,LTDataType x)
{assert(phead);assert(!IsEmpty(phead));LTNode* pos = phead->next;while (pos != phead){if (pos->data == x){return pos;}pos = pos->next;}printf("没找到\n");exit(0);
}
从phead->next的位置开始遍历,找到了则用pos返回该结点的地址,使用者可以用一个ret指针在外部接收,找不到则终止程序。查找前先确认链表不为空。

通过调试观察到,ret拿到了值为3的结点的地址

还可以继续展开,观察带头双向循环链表的内部结构,是怎样完成循环的。
2、insert指定位置前插入
//pos位置之前插入
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = BuyLTNode(x);LTNode* prevptr = pos->prev;prevptr->next = newnode;newnode->next = pos;newnode->prev = prevptr;pos->prev = newnode;}
先用prevptr找到pos的前一个结点,然后利用4个指针建立newnode与这两个结点的关系,完成中间位置插入。

可以用pos->prev指针直接找到前一个结点,不需要查找外的额外遍历,提高了效率。

3、erase指定位置删除
//pos位置删除
void LTErase(LTNode* pos)
{assert(pos);//LTNode* prevpos = pos->prev;//LTNode* nextpos = pos->next;//prevpos->next = nextpos;//nextpos->prev = prevpos;//free(pos);//pos = NULL;pos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}
同样找到pos前一个位置的结点,连接pos前一个与后一个结点,然后free掉pos

4、优化头尾操作
只要有了insert和erase两个函数,就可以轻易完成头尾的4个操作函数,只需要复用上述两个即可
例如在pos前插入一个节点,实际效果为尾插。
insert(pos->next)效果为头插
erase(phead->next)为头删
erase(phead->prev)为尾删

8、链表的销毁
//销毁
void LTDestroy(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){LTNode* next = cur->next;free(cur);cur = next;}free(phead);printf("销毁成功\n");
}
从phead->next位置开始遍历销毁即可,最后使用者在外部对创建的链表手动置空

掌握了以上操作,20分钟写一个链表不是难事
目录
1、链表种类大全
2、接口函数
3、初始化
4、打印
5、尾部操作
1、创建新结点
2、判断链表是否为空
3、尾插
4、尾删
6、头部操作
1、头插
2、头删
7、查找及指定位置pos操作
1、查找函数
2、insert指定位置前插入
3、erase指定位置删除
4、优化头尾操作
8、链表的销毁
相关文章:
带头双向循环链表及链表总结
1、链表种类大全 1、链表严格来说可能用2*2*28种结构,从是否带头,是否循环,是否双向三个角度区分。 2、无头单向循环链表一般不会在实际运用中直接存储数据,而会作为某些更复杂结构的一个子结构,毕竟它只在头插、头删…...
(八十)MySQL是如何基于各种规则去优化执行计划的?(中)
今天我们来讲一下子查询是如何执行的,以及他的执行计划是如何优化的。比如说类似于下面的SQL语句: select * from t1 where x1 (select x1 from t2 where idxxx) 这就是一个典型的子查询 也就是说上面的SQL语句在执行的时候,其实会被拆分为…...
第一章:命题与命题公式
1.命题与命题联结词 1.命题与命题的表示 1. 命题 由一个或几个已知的前提,推导出来一个未知的结论的思维过程称为推理,推理的基本要素就是表达这些前提的一些陈述句,可以将这些陈述句理解为命题。 (1)地球是行星 (2)8不是素数 (3)1 + 2 = 22. 命题真值 一个陈述句不…...
c/c++开发,无可避免的操作符operator(篇一),操作符重载
一、操作符号重载 虽然c/c内置了大量各类操作符,开发者可以很方便将其应用数学运算、成员访问、类型转换、内存分配等执行语句中,但很多时候,也需要根据项目应用需要,通过操作符重载,能够针对类类型的操作数定义不同的…...
【7.MySQL行格式存储】
1.MySQL数据存放文件 我们每创建一个 database(数据库) 都会在 /var/lib/mysql/ 目录里面创建一个以 database 为名的目录,创建一个student表 [rootxiaodainiao ~]#ls /var/lib/mysql/my_test db.opt student.frm student.ibddb.opt:用…...
【Linux】线程实例 | 简单线程池
今天来写一个简单版本的线程池 1.啥是线程池 池塘,顾名思义,线程池就是一个有很多线程的容器。 我们只需要把任务交到这个线程的池子里面,其就能帮我们多线程执行任务,计算出结果。 与阻塞队列不同的是,线程池中内有…...
ATAC-seq 数据分析实战
文章目录一、 ATAC-seq原理和基础知识1. ATAC-seq原理2. Tn5转座子1. 转座概念2. 参与分子1. 转座子(1) 简化的转座子结构(2) Tn5转座子的结构2. 转座酶3. 转座过程二、数据比对和过滤一、 ATAC-seq原理和基础知识 1. ATAC-seq原…...
设计模式-第13章(状态模式)
状态模式状态模式状态模式的好处和用处工作状态状态模式 状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况…...
ReentrantLock源码分析(一)加锁流程分析
一、ReetrantLock的使用示例 static ReentrantLock lock new ReentrantLock(); public static void main(String[] args) throws InterruptedException { new Thread(ClassLayOutTest::reentrantLockDemo, "threadA").start(); Thread.sleep(1000);…...
【C++】list的模拟实现
文章目录1.list 底层2. list的模拟实现1. list_node 类设计2. list类如何调用类型3 .push_back(正常实现)4. 迭代器的实现第一个模板参数Tconst迭代器第二个模板参数Ref第三个模板参数Ptr对list封装的理解5. insert6.push_back与 push_front(复用)7. erase8. pop_back与pop_fro…...
Python连接es笔记三之es更新操作
这一篇笔记介绍如何使用 Python 对数据进行更新操作。 对于 es 的更新的操作,不用到 Search() 方法,而是直接使用 es 的连接加上相应的函数来操作,本篇笔记目录如下: 获取连接update()update_by_query()批量更新UpdateByQuery()…...
哪个牌子的蓝牙耳机音质好?音质比较好的蓝牙耳机排名
蓝牙耳机经过多年发展,无论是在外观设计还是性能配置上都有很大的进步,越来越多的蓝牙耳机开始注重音质表现,逐渐有HIFI音质、无损音质出现在大众视野。那么哪个牌子的蓝牙耳机音质好?接下来,我来给大家分享几款音质比…...
Qt实用技巧:Qt中浮点数的相等比较方式(包括单精度和双精度)
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/129464152 红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…...
【数据结构初阶】双向循环链表
目录一.链表的分类二.与单链表相比三.实现增删查改1.双向循环链表结构的创建2.创建新节点3.初始化链表4.头插和尾插5.判断链表是否为空6.头删和尾删7.打印函数8.查找函数9.删除pos位置节点10.在pos前位置插入数据11.优化升级一.链表的分类 链表可有根据单向双向、有无哨兵位、…...
0104BeanDefinition合并和BeanClass加载-Bean生命周期详解-spring
文章目录1 前言2 BeanDefinition合并2.1 BeanDefinition合并在做什么?2.2 BeanDefinition怎么合并2.3 示例演示3 Bean Class 加载后记1 前言 下面要介绍的阶段,都是在调用getBean()从容器中获取bean对象的过程中发生的操作,我们需要更多的去…...
Java集合进阶(三)
文章目录一、Map1. 概述2. 基本功能3. 遍历4. 遍历学生对象5. 集合嵌套6. 统计字符出现次数二、Collections1. 常用方法2. 学生对象排序三、模拟斗地主一、Map 1. 概述 Interface Map<K, V>:K 是键的类型,V 是值的类型。 将键映射到值的对象&…...
【网络】什么是RPC?RPC与HTTP有什么关系?
文章目录RPC是什么RPC和HTTP的关系和区别[附]关于REST论文中提到的"HTTP不是RPC"重点参考 凤凰架构-远程过程调用 既然有HTTP为什么还要有RPC? RPC是什么 RPC(Remote Procedure Call):即远程过程调用,目的是为了让计算机能够跟调用…...
[手撕数据结构]栈的深入学习-java实现
CSDN的各位uu们你们好,今天千泽带来了栈的深入学习,我们会简单的用代码实现一下栈, 接下来让我们一起进入栈的神奇小世界吧!0.速览文章一、栈的定义1. 栈的概念2. 栈的图解二、栈的模拟实现三.栈的经典使用场景-逆波兰表达式总结一、栈的定义 1. 栈的概念 栈:一种…...
2.线性表的顺序表示
数据结构很重要! 数据结构很重要!!! 数据结构很重要!!!! 思考 1.线性表的顺序表示内容有哪些?(What) 2.为什么要学线性表的顺序表示? ? (Why)…...
eps文件删除了能恢复吗?恢复误删eps文件的三种方法
eps文件格式专为矢量图像和图形而设计。虽然没有被广泛使用,但它仍然受到各种插画家和平面设计师的钟爱。eps文件十分适合创建徽标和商标设计,主要应用见于广告牌、海报和横幅。可是在使用设备过程中,难免会遇到数据丢失问题,如果…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
Sklearn 机器学习 缺失值处理 获取填充失值的统计值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...
Linux-进程间的通信
1、IPC: Inter Process Communication(进程间通信): 由于每个进程在操作系统中有独立的地址空间,它们不能像线程那样直接访问彼此的内存,所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...
