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

文章目录

    • 一.头结点
    • 二.双链表
      • 1·双链表的概念与结构
      • 2.与单链表相比
    • 三.循环链表
      • 1.关于循环链表
      • 2.循环链表的优点
    • 四.带头双向循环链表
      • 1.带头双向循环链表
      • 2.结构图
      • 3.实现
    • 五.代码一览


一.头结点

在链表中设置头结点的作用是什么

标识链表:头结点是链表的特殊节点,它的存在能够明确标识出这是一个链表。在链表中,头结点通常不包含任何数据,它的主要作用是作为链表的入口,使得链表的操作更加方便。
简化操作:头结点的存在可以简化链表的操作。例如,当我们需要遍历整个链表时,只需要从头结点开始即可,无需关心链表的起始位置。同时,头结点的存在也使得在链表末尾插入或删除节点等操作更加方便。
提高效率:头结点的存在可以提高链表操作的效率。由于头结点是链表的第一个节点,因此在遍历链表时,我们无需担心指针的移动方向问题。同时,由于头结点在链表中的特殊位置,当需要访问链表的第一个元素时,可以通过直接访问头结点来获取,无需遍历整个链表。

在这里插入图片描述

二.双链表

1·双链表的概念与结构

概念:双链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针双向链接次序实现的。

结构图:
在这里插入图片描述

2.与单链表相比

双向性:双链表支持在每个节点上存储前驱节点和后继节点的指针,使得在任何节点上都可以方便地找到其前驱节点和后继节点。而单链表只能通过遍历整个链表来查找特定节点的下一个节点或上一个节点,效率较低。
插入和删除操作更高效:由于双链表支持双向链接,因此在插入和删除操作中,双链表只需要重新调整相关的指针即可,而不需要像单链表那样需要遍历整个链表。这使得双链表的插入和删除操作更高效。

三.循环链表

1.关于循环链表

循环链表是一种特殊的链表,其中最后一个节点指向第一个节点,即起始节点。起始节点充当列表开头的参考点。

(1)遍历时,可以从任何节点开始并以任何方向向前或向后遍历列表,直到到达开始的同一节点。
(2)循环链表没有开始也没有结束。
(3)在循环链表中,最后一个节点地址部分保存第一个节点的地址,从而形成一个循环链状结构。

(4).结构图:
在这里插入图片描述

2.循环链表的优点

(1)内存利用率是循环链表的共同优势之一
与线性数据结构不同,循环链表可以让人有效地使用内存,因为链表的大小动态增加或减少,因此不会浪费内存。此外,无需预先分配内存。
(2)实施
由于能够利用内存和易于数据操作,像堆栈和队列这样的线性数据结构通常可以使用链表轻松实现。
(3)易于数据操作
可以有效地处理循环链表的插入和删除,而无需重新构造链表。插入或删除元素后无需移动元素,只需更新下一个指针中存在的地址。

四.带头双向循环链表

1.带头双向循环链表

结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

2.结构图

在这里插入图片描述

3.实现

思路:
(1)利用结构体存储数据和指针(结构体能够存储不同类型的数据)
(2)通过malloc函数进行申请空间
(3)通过多条件实现
(4)我们实现初始化、打印、申请空间、尾插、头插等函数
框架:
结构体的创建、一些定义等
List.h

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int LTDataType;typedef struct ListNode
{struct ListNode* next;struct ListNode* prev;LTDataType data;
}ListNode;

Test.c

void Test() {ListNode* plist = ListInit()初始化,头结点不存储数据;}
int main() {Test();return 0;
}

函数实现:
(1)申请空间函数:
返回值:一个结构体指针
参数:传来的数据
通过malloc函数实现扩容,并将扩容后的指针置空和存储传来的数据
代码:

ListNode* BuyListNode(LTDataType x) {ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));//申请空间newnode->data = x;//存储数据newnode->next = NULL;//指针置空newnode->prev = NULL;return newnode;//返回指针
}

(2)初始化函数
我们开始要初始化一个头结点(不存储数据)
返回:一个指针(我们的头结点)
代码:

ListNode* ListInit() {//初始化ListNode* phead = BuyListNode(0);//第一个数据不打印phead->next = phead;//只有一个头结点,因为是双向循环链表,自己连接自己phead->prev = phead;return phead;
}

如图:
在这里插入图片描述
打印函数
代码:

void ListPrint(ListNode* phead) {assert(phead);//判断是否为空//初始条件ListNode* cat = phead->next;//打印出头结点next指向的结构体开始//结束条件while (cat!= phead)//当打印完一次后就会遇上同结点,此时结束打印{printf("%d ", cat->data);//迭代条件cat = cat->next;}printf("\n");
}

(3)尾插函数
我们要将一个数据尾插进去
第一步:找到这个链表的结尾处,我们可以通过头结点找到
第二步:我们重新将结尾处结构体的next指向尾插进来的结构体,再将尾插进来的结构体的prev指向结尾处的结构体,再再将尾插进来的结构体的next指向头结点,再再再将头结点的perv指向尾插进来的结构体
插前:
在这里插入图片描述
插后:
在这里插入图片描述
代码:

void ListPushBack(ListNode* phead, LTDataType x) {//尾插assert(phead);//判断是否为空ListNode* newnode = BuyListNode(x);//申请空间ListNode* cat = phead->prev;//找到尾结点并保存//重新连接cat->next = newnode;newnode->prev = cat;phead->prev = newnode;newnode->next = phead;
}

检查:
尾插 1 、2 、3

ListNode* plist = ListInit();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);

运行结果:
在这里插入图片描述
(4)头插函数
我们要将一个数据尾插进去
第一步:通过头结点找到第一个存储数据的点
第二步:我们重新将第一存储数据的结点处结构体的prev指向头插进来的结构体,再将头插进来的结构体的next指向第一个存储数据的结构体,再再将头插进来的结构体的prtv指向头结点,再再再将头结点的next指向头插进来的结构体

插前:
在这里插入图片描述
插后:
在这里插入图片描述
代码:

void ListPushFront(ListNode* phead, LTDataType x) {//头插assert(phead);ListNode* newnode = BuyListNode(x);ListNode* cat = phead->next;//找到头结点并保存//重新连接cat->prev = newnode;newnode->next = cat;phead->next = newnode;newnode->prev = phead;
}

检查:

头插 0
ListNode* plist = ListInit();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushFront(plist, 0);

运行结果:
在这里插入图片描述
(5)头删函数
我们想要删除第一个数据
第一步:通过头结点找到第一个存储数据的结点,再通过第一个存储数据的结点来找到第二个存储数据的结点
第二步:将第一个存储数据的结点释放掉,然后再将第二个存储数据的结点的prev指向头结点,再再头结点的next指向第二个存储数据的结点
删前:
在这里插入图片描述
删后:
在这里插入图片描述

代码:

void ListPopFront(ListNode* phead) {//头删assert(phead);assert(phead->next != phead);//ListNode* cat = phead->next->next;//通过第一个存储结点找到第二个存储的结点并保存free(phead->next);//释放掉phead->next = NULL;//并置空//重新连接cat->prev = phead;phead->next = cat;}

检查:
头删一个数据

ListNode* plist = ListInit();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushFront(plist, 0);
ListPopFront(plist);

运行结果:

在这里插入图片描述
(6)尾删函数
我们将最后一个结点删除
第一步:通过头结点来找到最后一个结点,再通过最后一个结点来找到倒数第二个结点
第二步:将最后一个结点释放掉,再将倒数第二个结点的next指向头结点,再再将头结点的prev指向倒数第二个结点
删前:
在这里插入图片描述
删后:
在这里插入图片描述
检查
尾删一个数据

ListNode* plist = ListInit();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushFront(plist, 0);
ListPopFront(plist);
ListPopBack(plist);
ListPrint(plist);

运行结果:
在这里插入图片描述

五.代码一览

List.h:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int LTDataType;typedef struct ListNode
{struct ListNode* next;struct ListNode* prev;LTDataType data;
}ListNode;ListNode* ListInit();//初始化
void ListDestory(ListNode* phead);//清空
void ListPrint(ListNode* phead);//打印void ListPushBack(ListNode* phead, LTDataType x);//尾插
void ListPushFront(ListNode* phead, LTDataType x);//头插
void ListPopFront(ListNode* phead);//头删
void ListPopBack(ListNode* phead);//尾删

List.c:

#define _CRT_SECURE_NO_WARNINGS
#include"List.h"ListNode* BuyListNode(LTDataType x) {//申请空间ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}ListNode* ListInit() {//初始化ListNode* phead = BuyListNode(0);phead->next = phead;phead->prev = phead;return phead;
}void ListPrint(ListNode* phead) {assert(phead);//初始条件ListNode* cat = phead->next;//结束条件while (cat!= phead){printf("%d ", cat->data);//迭代条件cat = cat->next;}printf("\n");
}
void ListPushBack(ListNode* phead, LTDataType x) {//尾插assert(phead);ListNode* newnode = BuyListNode(x);ListNode* cat = phead->prev;cat->next = newnode;newnode->prev = cat;phead->prev = newnode;newnode->next = phead;
}
void ListPushFront(ListNode* phead, LTDataType x) {//头插assert(phead);ListNode* newnode = BuyListNode(x);ListNode* cat = phead->next;cat->prev = newnode;newnode->next = cat;phead->next = newnode;newnode->prev = phead;
}
void ListPopFront(ListNode* phead) {//头删assert(phead);assert(phead->next != phead);ListNode* cat = phead->next->next;free(phead->next);phead->next = NULL;cat->prev = phead;phead->next = cat;}
void ListPopBack(ListNode* phead) {//尾删assert(phead);assert(phead->prev != phead);ListNode* cat = phead->prev->prev;free(phead->prev);phead->prev = NULL;cat->next = phead;phead->prev = cat;
}

Test.c

void Test() {ListNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushFront(plist, 0);ListPopFront(plist);ListPopBack(plist);ListPrint(plist);}int main() {Test();return 0;
}

以上就是全部代码了
还有一些查改等接口函数没实现,感兴趣的可以去试试

以上就是我的分享了,如果有什么错误,欢迎在评论区留言。
最后,谢谢大家的观看!

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

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

相关文章

springboot基于web的网上摄影工作室的开发与实现论文

网上摄影工作室 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了网上摄影工作室的开发全过程。通过分析网上摄影工作室管理的不足&#xff0c;创建了一个计算机管理网上摄影工作室的方案。文章介绍了网上摄影工…

1.亿级积分数据分库分表:总体方案设计

项目背景 以一个积分系统为例&#xff0c;积分系统最核心的有积分账户表和积分明细表&#xff1a; 积分账户表&#xff1a;每个用户在一个品牌下有一个积分账户记录&#xff0c;记录了用户的积分余额&#xff0c;数据量在千万级积分明细表&#xff1a;用户每次积分发放、积分扣…

gitlab添加ssh公钥

一&#xff1a;生成公钥 桌面鼠标右击打开 Open Git Bash here (前提是安装了Git)&#xff1b; 2.输入命令 ssh-keygen -t rsa -C "123*****90qq.com"来生成新的密钥对,将其中的"123*****90qq.com"替换为你自己的电子邮件地址。 命令&#xff1a;ssh-keyg…

MWC 2024丨美格智能推出5G RedCap系列FWA解决方案,开启5G轻量化新天地

2月27日&#xff0c;在MWC 2024世界移动通信大会上&#xff0c;美格智能正式推出5G RedCap系列FWA解决方案。此系列解决方案具有低功耗、低成本等优势&#xff0c;可以显著降低5G应用复杂度&#xff0c;快速实现5G网络接入&#xff0c;提升FWA部署的经济效益。 RedCap技术带来了…

Linux之嫁衣神功

前言&#xff1a;此博客内容全部转载他人&#xff0c;无一原创&#xff0c;初衷转播优质内容 1 挂载的作用 扩展存储空间 将额外的存储设备连接到Linux系统中&#xff0c;扩展系统的存储容量。 实现数据共享 不同计算机之间可以共享文件和数据&#xff0c;实现更高效的协作…

强大!信息安全技术导图全汇总!共200多张(附下载)

从网络上搜集整理了200多张信息安全技术导图&#xff0c;文末有免费领取方式。 详细文件目录 APT 攻击/ APT 攻击.png APT攻防指南基本思路v1.0-SecQuan.png Red Teaming Mind Map.png Windows常见持久控制.png 发现与影响评估.jpg …

Unity的相机跟随和第三人称视角

Unity相机跟随和第三人称视角 介绍镜头视角跟随人物方向进行旋转的镜头视角固定球和人的镜头视角 思路跟随人物方向进行旋转的镜头视角固定球和人的镜头视角 镜头旋转代码人物移动的参考代码注意 介绍 最近足球项目的镜头在做改动&#xff0c;观察了一下实况足球的视角&#x…

html基本标签

<h1></h1> <p></p> h是标签从h1~h6&#xff0c;没用h7,h8 p是段落 <a href"https://www.educoder.net">Educoder平台</a> href可以指定链接进行跳转 <img src"https://www.educoder.net/attachments/download/2078…

跨境知识分享:什么是动态IP?和静态IP有什么区别?

对于我们跨境人来说&#xff0c;清楚地了解IP地址、代理IP等这些基础知识&#xff0c;并学会正确地使用IP地址对于保障店铺的安全性和稳定性至关重要&#xff0c;尤其是理解动态IP和静态IP之间的区别&#xff0c;以及如何利用这些知识来防止账号关联&#xff0c;对于每个电商卖…

深入理解分库、分表、分库分表

前言 分库分表&#xff0c;是企业里面比较常见的针对高并发、数据量大的场景下的一种技术优化方案&#xff0c;所谓"分库分表"&#xff0c;根本就不是一件事儿&#xff0c;而是三件事儿&#xff0c;他们要解决的问题也都不一样&#xff0c;这三个事儿分别是"只…

Nodejs 第四十二章(jwt)

什么是jwt? JWT&#xff08;JSON Web Token&#xff09;是一种开放的标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在网络应用间传递信息的一种方式。它是一种基于JSON的安全令牌&#xff0c;用于在客户端和服务器之间传输信息。 https://jwt.io/ JWT由三部分组成&…

Qt 自定义长条进度条(类似播放器进度条)

1.运行界面 2.步骤 其实很简单。 2.1绘制底图圆角矩形 2.2绘制播放进度圆角矩形 参考&#xff1a;painter绘图 3.源码 #pragma once#include <QWidget> #include <QLabel> #include <QHBoxLayout> #include <QMouseEvent> #include <QDebug&g…

探索Redis 6.0的新特性

Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的内存中数据结构存储系统&#xff0c;通常被用作缓存、消息队列和实时数据处理等场景。它的简单性、高性能以及丰富的数据结构支持使其成为了众多开发者和企业的首选。在Redis 6.0版本中&#xff0c;引入了一…

NVMe开发——PCIe复位

简介 PCIe中有4种复位机制&#xff0c;早期的3种被称为传统复位(Conventional Reset)。传统复位中的前2种又称为基本复位(Fundamental Resets)&#xff0c;分别为冷复位(Cold Reset)&#xff0c;暖复位(Warm Reset)。第3种复位为热复位(Hot Reset)。第4种复位被称为功能级复位…

SQL面试题(2)

第一题 创建trade_orders表: create table `trade_orders`( `trade_id` varchar(255) NULL DEFAULT NULL, `uers_id` varchar(255), `trade_fee` int(20), `product_id` varchar(255), `time` varchar(255) )ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_…

【机器学习基础】层次聚类-BIRCH聚类

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; &#x1f4a1;专栏&#xff1a;机器学习 欢迎订阅&#xff01;相对完整的机器学习基础教学&#xff01; ⭐特别提醒&#xff1a;针对机器学习&#xff0c;特别开始专栏&#xff1a;机器学习python实战…

如何将视频的声音转换成音频?视频提取音频的小妙招

在数字化时代&#xff0c;视频和音频是我们生活中不可或缺的元素。有时候&#xff0c;我们可能只需要视频中的音频部分&#xff0c;这时就需要将视频的声音转换成音频文件。那么&#xff0c;如何实现这一操作呢&#xff1f;本文将为您介绍几种简单而实用的小妙招。 方法一&…

朱元璋如何处理十万女俘让蒙古人险些灭绝

明太祖朱元璋的铁血手腕&#xff1a;十万女俘与蒙古人的灭顶之灾 在中国历史上&#xff0c;明太祖朱元璋以其卓越的领导才能和深谋远虑的政治智慧&#xff0c;开创了明朝的辉煌篇章。然而&#xff0c;在他登基称帝的背后&#xff0c;却隐藏着一段鲜为人知的残酷往事。今天&…

抽象类、模板方法模式

抽象类概述 在Java中abstract是抽象的意思&#xff0c;如果一个类中的某个方法的具体实现不能确定&#xff0c;就可以申明成abstract修饰的抽象方法&#xff08;不能写方法体了&#xff09;&#xff0c;这个类必须用abstract修饰&#xff0c;被称为抽象类。 抽象方法定义&…

202435读书笔记|《半小时漫画中国史》——读点经济学与历史,生活更美好,趣味烧脑土地制度、商鞅变法、华丽丽的丝绸之路这里都有

202435读书笔记|《半小时漫画中国史》——读点经济学与历史&#xff0c;生活更美好&#xff0c;趣味烧脑土地制度、商鞅变法、华丽丽的丝绸之路这里都有 1. 土地政策、度量衡及税收2. 商鞅变法3. 西汉经济4. 西汉盐铁大辩论5. 西汉丝绸之路 《半小时漫画中国史&#xff1a;经济…