线性表之带头双向循环链表
目录
一、本章重点
二、带头双向循环链表介绍
2.1什么是带头双向循环链表?
2.2最常用的两种链表结构
三、带头双向循环链表常用接口实现
3.1结构体创建
四、实现接口总结
五、在线oj训练与详解
一、本章重点
- 带头双向循环链表介绍
- 带头双向循环链表常用接口实现
- 实现接口总结
- 在线oj训练与详解
二、带头双向循环链表介绍
2.1什么是带头双向循环链表?
- 带头:存在一个哨兵位的头节点,该节点是个无效节点,不存储任何有效信息,但使用它可以方便我们头尾插和头尾删时不用判断头节点指向NULL的情况,同时也不需要改变头指针的指向,也就不需要传二级指针了。
- 双向:每个结构体有两个指针,分别指向前一个结构体和后一个结构体。
- 循环:最后一个结构体的指针不再指向NULL,而是指向第一个结构体。(单向)
- 第一个结构体的前指针指向最后一个结构体,最后一个结构体的后指针指向第一个结构体(双向)。
图解
2.2最常用的两种链表结构
- 更具有无头,单双向,是否循环组合起来有8种结构,但最长用的还是无头单向非循环链表和带头双向循环链表
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
- 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。
三、带头双向循环链表常用接口实现
3.1结构体创建
typedef int DataType;
typedef struct DListNode
{DataType data;DListNode* prev;DListNode* next;
}DListNode;
3.2带头双向循环链表的初始化
void DListInint(DListNode** pphead)
{*pphead = (DListNode*)malloc(sizeof(DListNode));(*pphead)->next = (*pphead);(*pphead)->prev = (*pphead);
}
或者使用返回节点的方法也能实现初始化
DListNode* DListInit(){DListNode* phead = (DListNode*)malloc(sizeof(DListNode));phead->next = phead;phead->prev = phead;return phead;}
3.3创建新节点
DListNode* BuyDListNode(DataType x)
{DListNode* temp = (DListNode*)malloc(sizeof(DListNode));if (temp == NULL){printf("malloc fail\n");exit(-1);}temp->prev = NULL;temp->next = NULL;temp->data = x;return temp;
}
3.4尾插
void DListPushBack(DListNode* phead,DataType x)
{DListNode* newnode = BuyDListNode(x);DListNode* tail = phead->prev;tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}
3.5打印链表
void DListNodePrint(DListNode* phead)
{DListNode* cur = phead->next;while (cur != phead){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}
3.6头插
void DListNodePushFront(DListNode* phead, DataType x)
{DListNode* next = phead->next;DListNode* newnode = BuyDListNode(x);next->prev = newnode;newnode->next = next;newnode->prev = phead;phead->next = newnode;
}
3.7尾删
void DListNodePopBack(DListNode* phead)
{if (phead->next == phead){return;}DListNode* tail = phead->prev;DListNode* prev = tail->prev;prev->next = phead;phead->prev = prev;free(tail);tail = NULL;
}
3.8头删
void DListNodePopFront(DListNode* phead)
{if (phead->next == phead){return;}DListNode* firstnode = phead->next;DListNode* secondnode = firstnode->next;secondnode->prev = phead;phead->next = secondnode;free(firstnode);firstnode = NULL;
}
3.9查找data(返回data的节点地址)
DListNode* DListNodeFind(DListNode* phead, DataType x)
{DListNode* firstnode = phead->next;while (firstnode != phead){if (firstnode->data == x){return firstnode;}firstnode = firstnode->next;}return NULL;
}
3.10在pos位置之前插入节点
void DListNodeInsert(DListNode* pos, DataType x)
{DListNode* prev = pos->prev;DListNode* newnode = BuyDListNode(x);newnode->next = pos;newnode->prev = prev;prev->next = newnode;pos->prev = newnode;
}
3.11删除pos位置的节点
void DListNodeErase(DListNode* pos)
{DListNode* prev = pos->prev;DListNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);pos = NULL;
}
四、实现接口总结
- 多画图:能给清晰展示变化的过程,有利于实现编程。
- 小知识:head->next既可表示前一个结构体的成员变量,有可表示后一个结构体的地址。当head->next作为左值时代表的是成员变量,作右值时代表的是后一个结构体的地址。对于链表来说理解这一点非常重要。
- 实践:实践出真知
- 带头双向循环链表:相比于单链表,它实现起来更简单,不用向单链表一样分情况讨论链表的长度。虽然结构较复杂,但使用起来更简单,更方便。
五、在线oj训练与详解
5.1链表的中间节点(力扣)
给定一个头结点为
head
的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.来源:力扣(LeetCode)
思路:快慢指针
取两个指针,初始时均指向head,一个为快指针(fast)一次走两步,另一个为慢指针(slow)一次走一步,当快指针满足fast==NULL(偶数个节点)或者fast->next==NULL(奇数个节点)时,slow指向中间节点,返回slow即可。
struct ListNode* middleNode(struct ListNode* head)
{struct ListNode* fast=head;struct ListNode* slow=head;while(fast&&fast->next){fast=fast->next->next;slow=slow->next;}return slow;
}
线性表之带头双向循环链表相关推荐
- 比特数据结构与算法(第二章收尾)带头双向循环链表的实现
1.链表的分类 链表的分类 ① 单向或者双向 ② 带头或者不带头 ③ 循环或者非循环 常用的链表: 根据上面的分类我们可以细分出8种不同类型的链表,这么多链表我们一个个讲解这并没有意义.我们实际中最常 ...
- 【数据结构】-关于带头双向循环链表的增删查改
作者:低调 作者宣言:写好每一篇博客 文章目录 前言 一.带头双向循环链表的实现 1.1创建返回链表的头结点 1.2开辟一个新的结点 1.3双向链表的销毁 1.4双向链表的打印 1.5双向链表尾插 1 ...
- 一文搞定带头双向循环链表
什么是链表 链表的种类 最经典的两种链表 带头双向循环链表详细 什么是链表 在数据结构线性表里 除了顺序表 还有一种我们不得不说 他就是链表 链表的关键就是指针 创建出一块节点空间存储着 一边是val ...
- 数据结构与算法 | 带头双向循环链表
上一节里实现的是最简单的链表,在实际中那种链表不会单独用来存储数据,更多是作为其他数据结构的子结构,如图的邻接表等.而比较常用的就是带头双向循环链表. 通过对比我们可以看出有三个不同,多了头节点,链表 ...
- 【数据结构】链表:带头双向循环链表的增删查改
本篇要分享的内容是带头双向链表,以下为本片目录 目录 一.链表的所有结构 二.带头双向链表 2.1尾部插入 2.2哨兵位的初始化 2.3头部插入 2.4 打印链表 2.5尾部删除 2.6头部删除 2. ...
- 数据结构-带头双向循环链表(增删查改详解)
在上一篇博客中,详细介绍了单链表的增删查改,虽然单链表的结构简单,但是用起来却不是那么顺手.因此根据单链表的种种缺点,这篇博客所介绍的带头双向循环链表将会带来极大的优化. 上图就是带头双向循环链表的主 ...
- 【数据结构】带头双向循环链表
各位读者们好久不见了,咋们接着上一期链表来,今天来实现一下链表最难的结构,同时也是实现起来最简单的结构--带头双向循环链表.话不多说,进入主题 文章目录 前言 实现带头双向循环链表 DList.h头文 ...
- 【数据结构初阶】链表(下)——带头双向循环链表的实现
目录 带头双向循环链表的实现 1.带头双向循环链表的节点类型 2.创建带头双向循环链表的节点 3.向带头双向循环链表中插入数据 <3.1>从链表尾部插入数据 <3.2>从链表头 ...
- 实现简单的带头双向循环链表
双向链表 1. 带头双向循环链表的定义 2. 带头双向循环链表的创建 3. 带头双向循环链表的增删改查 (1)头插头删 (2)尾插尾删 (3)pos位置的前插与删除 4.插入与删除改良 1. 带头双向 ...
- c语言实现数据结构中的带头双向循环链表
目录标题 一.单向链表的不足 二.带头双向链表的准备 三.带头双向链表的初始化 四.带头双向链表的尾插 五.带头双向链表的打印 六.带头双向链表头插 七.判断链表是否为空 八.带头双向链表尾删 九.带 ...
最新文章
- 预告 · Flutter Live 2018 全球同步直播
- XCOM串口助手打印不出数据
- 基于MATLAB的语音信号的时域特性分析(一)——分帧、窗函数
- aws python lambda_AWS Lambda
- red hat 5 和 oracle
- SpringOne Platform 2016回顾
- 上传文件Base64格式(React)
- python api测试框架_python api 测试框架
- django 1.8 官方文档翻译: 14-4-1 重定向应用
- radvd移植到arm交叉编译问题解决
- VB6的后期绑定和前期绑定
- [转载] python enumerate函数 实例_python中使用enumerate函数遍历元素实例
- 知乎:学习分布式系统需要怎样的知识?
- matpower安装问题
- 无线通信设备安装工程概预算编制_起重设备安装资质承接多大工程
- 【工具】——远程协助(向日葵)
- 如何删除ie浏览器缓存文件、缓存js
- java-数组-数组缩减
- 媒体专访 | 许彬教授:我们离元宇宙Big Bang有多近?
- 【NLP】文本分类算法-基于字符级的无词嵌入双向循环神经网络(双向 GRU)
热门文章
- 图片飞出去的特效怎么做?3分钟AE制作教程
- 红巨星粒子Red Giant Trapcode Suite
- 【2020】微软 MCSA,MCSD,MCSE认证于2021年1月31停用,此后您将无法再获得此认证 - GJYJSJGS - 高级云计算架构师
- jquery版本问题
- DIY 3D打印机测试
- 微信Emoji工具-EmojiUtil
- matlab牛顿插值法
- Linux部署django项目最全,linux部署django项目流程(全)
- 360手机助手电脑版 v2.4.0.1251 官方版
- m3u8 ts文件利用系统工具COPY合并序列TS文件