【数据结构】双向带头循环链表

⭐️ 往期相关文章

✨链接1:数据结构和算法的概念以及时间复杂度空间复杂度详解
✨链接2:【数据结构】手撕顺序表(动态版)+代码详解
✨链接3:【数据结构】手撕单链表+代码详解


⭐️ 双向带头循环链表

图解:
请添加图片描述
双向: 链表内定义两个指针,一个 prev 一个 nextprev 指向上一个结点的地址,next 指向下一个结点的地址。
带头: 指的是链表带哨兵卫的头结点,哨兵卫的头结点不存储有效的数据,有了哨兵卫的头结点在特定的情况下可以大大的降低代码的复杂度(如单链表中的尾插,当链表为空的时候,尾插不需要判断 phead == NULL)。
循环:pheadprev 指向最后一个结点的地址,最后一个结点的 next 指向 phead

双向带头循环链表是最优的一种链表结构,无论头插尾插头删尾删任意位置的删除插入,时间复杂度都是 O ( 1 ) O(1) O(1),它不像单链表中有很多的缺陷,而双向带头循环链表更适合存储数据。

🌠 双向带头循环链表与顺序表之间的区别

不同点顺序表链表
存储空间上物理上地址是连续的逻辑上连续,但是物理不一定连续
🌟随机访问支持 O ( 1 ) O(1) O(1)不支持 O ( N ) O(N) O(N)
任意位置的插入删除数据可能需要移动元素 O ( N ) O(N) O(N)只需要改变指针指向 O ( 1 ) O(1) O(1)
增加元素动态顺序表,空间不够需要扩容按需申请空间释放空间
应用场景需要随机访问任意位置插入删除频繁
缓存利用率

缓存利用率解释: 在计算机存储中,有一个金字塔模式。从下到上速度越快但是容量越小成本越高。

  • 寄存器
  • 高速缓存
  • 内存
  • 磁盘
  • 网盘

由于寄存器的速度过快,内存跟不上寄存器的速度,所有在中间引入了高速缓存,当寄存器操作时会先去高速缓存中读取数据,如果高速缓存中没有数据则去内存中读取,但是这里有一个局部命中原则,每次读取到不是一个数据而是会把这个数据后面的地址连续的数据都读取到高速缓存中,所以由于顺序表物理地址是连续的,所以后面的元素容易被命中,则下次操作后面的元素,寄存器可以直接到高速缓存中读取到。

🌠 手撕链表

接口函数

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int DoubleListType;typedef struct DoubleList {DoubleListType data;		// 链表的数据struct DoubleList* prev;	// 链表的前驱指针struct DoubleList* next;	// 链表的后继指针
}DoubleList;// 创建结点
DoubleList* CreateNode(DoubleListType node);
// 初始化
DoubleList* DoubleListInit();
// 销毁
void DoubleListDestroy(DoubleList* phead);
// 打印
void DoubleListPrint(DoubleList* phead);
// 尾插
void DoubleListPushBack(DoubleList* phead , DoubleListType node);
// 头插
void DoubleListPushFront(DoubleList* phead, DoubleListType node);
// 尾删
void DoubleListPopBack(DoubleList* phead);
// 头删
void DoubleListPopFront(DoubleList* phead);
// 查找
DoubleList* DoubleListFind(DoubleList* phead , DoubleListType node);
// 在 pos 之前插入
void DoubleListInsert(DoubleList* pos , DoubleListType node);
// 删除 pos 位置
void DoubleListErase(DoubleList* pos);
// 大小
int DoubleListSize(DoubleList* phead);

CreateNode 实现:

DoubleList* CreateNode(DoubleListType node) {DoubleList* newNode = (DoubleList*)malloc(sizeof(DoubleList));assert(newNode);newNode->data = node;newNode->prev = NULL;newNode->next = NULL;return newNode;
}

DoubleListInit 实现:

// 创建带哨兵卫的头结点
DoubleList* DoubleListInit() {DoubleList* pList = CreateNode(-1);pList->prev = pList;pList->next = pList;return pList;
}

DoubleListDestroy 实现:

void DoubleListDestroy(DoubleList* phead) {assert(phead);DoubleList* cur = phead->next;while (cur != phead) {DoubleList* curNext = cur->next;free(cur);cur = curNext;}// cur == pheadfree(cur);
}

DoubleListPrint 实现:

void DoubleListPrint(DoubleList* phead) {assert(phead);DoubleList* cur = phead->next;while (cur != phead) {printf("%d " , cur->data);cur = cur->next;}printf("\n");
}

DoubleListPushBack 实现:

void DoubleListPushBack(DoubleList* phead, DoubleListType node) {assert(phead);DoubleList* tail = phead->prev;DoubleList* newNode = CreateNode(node);tail->next = newNode;newNode->prev = tail;newNode->next = phead;phead->prev = newNode;
}

DoubleListPushFront 实现:

void DoubleListPushFront(DoubleList* phead, DoubleListType node) {assert(phead);DoubleList* headNext = phead->next;DoubleList* newNode = CreateNode(node);phead->next = newNode;newNode->next = headNext;newNode->prev = phead;headNext->prev = newNode;
}

DoubleListPopBack 实现:

void DoubleListPopBack(DoubleList* phead) {assert(phead);// 空链表assert(phead->next != phead);DoubleList* tail = phead->prev;DoubleList* tailPrev = tail->prev;tailPrev->next = phead;phead->prev = tailPrev;free(tail);
}

DoubleListPopFront 实现:

void DoubleListPopFront(DoubleList* phead) {assert(phead);// 空链表assert(phead->next != phead);DoubleList* headNext = phead->next;DoubleList* headNextNext = headNext->next;phead->next = headNextNext;headNextNext->prev = phead;free(headNext);
}

DoubleListFind 实现:

DoubleList* DoubleListFind(DoubleList* phead, DoubleListType node) {assert(phead);DoubleList* cur = phead->next;while (cur != phead) {if (cur->data == node) {return cur;}cur = cur->next;}return NULL;
}

DoubleListInsert 实现:

void DoubleListInsert(DoubleList* pos, DoubleListType node) {assert(pos);DoubleList* posPrev = pos->prev;DoubleList* newNode = CreateNode(node);posPrev->next = newNode;newNode->prev = posPrev;newNode->next = pos;pos->prev = newNode;
}

DoubleListErase 实现:

void DoubleListErase(DoubleList* pos) {assert(pos);DoubleList* posPrev = pos->prev;DoubleList* posNext = pos->next;posPrev->next = posNext;posNext->prev = posPrev;free(pos);
}

DoubleListSize 实现:

// 不能使用哨兵卫的头结点存储数据的长度,因为当数据类型不是 int 时,会有问题
int DoubleListSize(DoubleList* phead) {assert(phead);int size = 0;DoubleList* cur = phead->next;while (cur != phead) {size++;cur = cur->next;}return size;
}

🌠 面试官问:你可以10分钟写一个链表吗?
🌠 答:其实是可以的!

当实现 DoubleListInsertDoubleListErase 函数,其他接口可以复用这两个函数。

void DoubleListPushBack(DoubleList* phead, DoubleListType node) {assert(phead);// 复用DoubleListInsert(phead , node);
}
void DoubleListPushFront(DoubleList* phead, DoubleListType node) {assert(phead);// 复用DoubleListInsert(phead->next , node);
}
void DoubleListPopBack(DoubleList* phead) {assert(phead);// 空链表assert(phead->next != phead);// 复用DoubleListErase(phead->prev);
}
void DoubleListPopFront(DoubleList* phead) {assert(phead);// 空链表assert(phead->next != phead);// 复用DoubleListErase(phead->next);
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LVS简介及LVS-NAT负载均衡群集的搭建

目录 一、LVS群集简介 1.群集的含义和应用场景 2.性能扩展方式 3.群集的分类 负载均衡&#xff08;LB&#xff09; 高可用&#xff08;HA&#xff09; 高性能运算&#xff08;HPC&#xff09; 二、LVS负载均衡群集简介及搭建 1.负载均衡群集架构 2.三种工作模式 3.启…

C++教程(一)开发环境visual studio的安装——图文详细

一、visual studio下载地址&#xff1a; 1、百度网盘 链接&#xff1a;https://pan.baidu.com/s/1QJosSoAT7EumuvyjtC_1Iw?pwdwuqz 提取码&#xff1a;wuqz 2、官网下载 Visual Studio: 面向软件开发人员和 Teams 的 IDE 和代码编辑器 (microsoft.com)https://visualstudio.…

python flask sqlite http服务

python flask 服务 为了让拼接项目能够简单实用http服务&#xff0c;用python flask 来做一个http服务&#xff0c;使用python的原因是拼接项目本身很多人工智能的服务是用python来写。数据库为了简单实用sqlite&#xff0c;python自带sqlite3 ,很方便 db.sql CREATE TABLE …

uni-app

uni-app 一、准备工作1.新建项目2.配置浏览器3.兼容4.新建页面 二、上手1.pages.json文件的页面配置与全局配置2.rpx尺寸单位3.内置组件4.vue2写法&#xff08;1&#xff09;模板&#xff08;2&#xff09;插值语法&#xff08;3&#xff09;指令&#xff08;4&#xff09;styl…

猿人学web刷题1

1.第一题 js混淆源码乱码 - 猿人学 : url 时间戳加密 右键遇到反调试&#xff0c;参考前面的文章 过反调试 2.ast解混淆 首页1.js 拿到 function oo0O0, 在<script>标签里面, 无法调试&#xff0c;分析自己提取出来&#xff0c;或则hook替换 加密逻辑存在于window.a中&a…

分布式概念

分布式系统中的相关概念 1 大型互联网项目架构目标 2 集群和分布式 替换模块 3 架构演进

代码随想录day11

20. 有效的括号 思路&#xff1a;这里用模拟栈的方法会更好理解&#xff0c;也就是我们每次遇到朝左方向的三种类型的时候&#xff0c;就加入相反方向的右括号到result栈中。由于栈是一个先进后出的方式&#xff0c;所以我们会有一个判断stack当前为不为空&#xff0c;和stack[…

大型语言模型与知识图谱协同研究综述:两大技术优势互补

机器之心报道 编辑&#xff1a;杜伟 多图综述理清当前研究现状&#xff0c;这篇 29 页的论文值得一读。 大型语言模型&#xff08;LLM&#xff09;已经很强了&#xff0c;但还可以更强。通过结合知识图谱&#xff0c;LLM 有望解决缺乏事实知识、幻觉和可解释性等诸多问题&am…

JVM源码剖析之SymbolTable和StringTable

很多读者在观看JVM相关的书籍时会看到SymbolTable和StringTable&#xff0c;书中的三言二语介绍的不是很清楚&#xff0c;并且读者的水平有限&#xff0c;导致无法理解SymbolTable和StringTable。所以特意写此篇图文并茂的文章来彻底理解SymbolTable和StringTable这两张表。 版…

mac电脑上,webm格式怎么转换成mp4?

mac电脑上&#xff0c;webm格式怎么转换成mp4&#xff1f;webm格式的视频也是最近几年也越来越多的&#xff0c;小编最近就不止一次的下载到过webm格式的视频&#xff0c;很多小伙伴肯定对它还并不是很了解&#xff0c;webm是由谷歌公司所提出以及开发出来的视频文件格式&#…

debian to go

可以使用虚拟机操作&#xff0c;在运行镜像到安装步骤时选择 u盘 不需要手动分 /boot 分区之类的&#xff0c;“Automaction”自动分区就行&#xff0c;全安装到根目录。boot load 安装到 /dev/sdb&#xff0c;也就是硬盘本身 推荐使用gpt分区表&#xff0c;建议拿不用的盘练…

MySQL表单查询

根据题目完成下列要求 CREATE TABLE emp ( empno int(4) NOT NULL, ename varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, job varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, mgr int(4) NULL DEFAULT …