(数据结构)双向链表

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

前言

  • 前面链表部分,咱们着重讲解了不带头单向不循环链表,简称单链表。那么链表其实也分很多种类适用于各种各样的场景。通过单链表的学习,其实我们已经大致了解了链表的绝大多数的内容,所以接下来我通过再为大家讲解一个带头双向循环链表,那么剩下的链表的种类大家就可以各自组合,各自书写了。
  • 链表种类:
    在这里插入图片描述
  • 两种代表链表:
  • 在这里插入图片描述
    1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
    2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带 来很多优势,实现反而简单了,后面我们代码实现了就知道了。

整体的实现代码如下:(头文件)

 #include<stdio.h>#include<stdlib.h>#include<assert.h>//对int类型取别名是为了后续方便更改数据类型typedef int LTDateType;//链表节点的结构typedef struct ListNode{struct ListNode *prev;struct ListNode *next;LTDateType date;}LTNode;//动态申请一个节点LTNode *BuyLTNode(LTDateType x);//对链表进行初始化LTNode *LTInit();//打印链表void LTPrint(LTNode* phead);//尾部插入数据void LTpushBack(LTNode *phead,LTDateType x);//尾部删除数据void LTpopBack(LTNode* phead);//头部插入数据void LTpushFront(LTNode *phead,LTDateType x);//头部删除数据void LTpopFront(LTNode* phead);//计算链表中有效数据的个数int LTSize(LTNode* phead);//查找数据值为x的节点LTNode *LTFind(LTNode *phead,LTDateType x);//在pos节点前插入数据为x的节点void LTInsert(LTNode* pos, LTDateType x);//删除pos位置处的节点void LTErase(LTNode* pos);//销毁链表void LTDestroy(LTNode* phead);
  • 函数实现文件:
 #include "List.h"LTNode *BuyLTNode(LTDateType x){LTNode *node= (LTNode*)malloc(sizeof (LTNode));if(node==NULL){perror("malloc fail");exit(-1);}node->date=x;node->next=NULL;node->prev=NULL;return node;}LTNode *LTInit(){LTNode *phead= BuyLTNode(-1);phead->prev=phead;phead->next=phead;return phead;}void LTPrint(LTNode* phead){assert(phead);printf("phead<->");LTNode *cur=phead->next;while(cur!=phead){printf("%d<->",cur->date);cur=cur->next;}printf("phead\n");}void LTpushBack(LTNode *phead,LTDateType x){//判断phead是否为空指针assert(phead);//尾节点如下,无需再去遍历寻找尾节点LTNode *tail=phead->prev;//创建一个新节点LTNode *newnode=BuyLTNode(x);//改变指针指向,将尾节点与新节点相连newnode->prev=tail;tail->next=newnode;newnode->next=phead;phead->prev=newnode;}void LTpopBack(LTNode* phead){assert(phead);if (phead->next == phead)exit(-1);else{LTNode *tail=phead->prev;phead->prev=tail->prev;phead->prev->next=phead;free(tail);}}void LTpushFront(LTNode *phead,LTDateType x){assert(phead);/*  LTNode *newnode= BuyLTNode(x);newnode->next=phead->next;phead->next->prev=newnode;phead->next=newnode;newnode->prev=phead;*/LTNode *newnode= BuyLTNode(x);LTNode *first=phead->next;phead->next=newnode;newnode->prev=phead;newnode->next=first;first->prev=newnode;}void LTpopFront(LTNode* phead){assert(phead);if (phead->next == phead)exit(-1);else{/* LTNode *first=phead->next;phead->next=first->next;phead->next->prev=phead;free(first);*/LTNode *first=phead->next;LTNode *second=first->next;free(first);phead->next=second;second->prev=phead;}}int LTSize(LTNode* phead){assert(phead);int size=0;LTNode *cur=phead->next;while(cur!=phead){size++;cur=cur->next;}return size;}LTNode *LTFind(LTNode *phead,LTDateType x){assert(phead);LTNode *cur=phead->next;while(cur!=phead){if(cur->date==x)return cur;cur=cur->next;}return NULL;}void LTInsert(LTNode* pos, LTDateType x){assert(pos);LTNode *posPrev=pos->prev;LTNode *newnode= BuyLTNode(x);posPrev->next=newnode;newnode->prev=posPrev;newnode->next=pos;pos->prev=newnode;}void LTErase(LTNode* pos){assert(pos);/*    pos->prev->next=pos->next;pos->next->prev=pos->prev;free(pos);*/LTNode *posPrev=pos->prev;LTNode *posNext=pos->next;free(pos);posPrev->next=posNext;posNext->prev=posPrev;}void LTDestroy(LTNode* phead){assert(phead);LTNode *cur=phead->next;while(cur!=phead){LTNode *next=cur->next;free(cur);cur=next;}free(phead);}
  • 测试文件:(这里的测试文件,其实就是大家调用写好的函数,看功能是否正确,大家可以自行更改,这里只是一个示例)
 #include "List.h"void test1(){LTNode *plist=LTInit();LTpushBack(plist,1);LTpushBack(plist,2);LTpushBack(plist,3);LTpushBack(plist,4);LTPrint(plist);int c= LTSize(plist);printf("%d\n",c);LTNode *node= LTFind(plist,3);LTInsert(node,6);LTErase(node);LTPrint(plist);LTDestroy(plist);plist=NULL;}int main(){test1();return 0;}

函数逐个讲解:

  1. 第一个动态申请链表节点:
 LTNode *BuyLTNode(LTDateType x){LTNode *node= (LTNode*)malloc(sizeof (LTNode));if(node==NULL){perror("malloc fail");exit(-1);}node->date=x;node->next=NULL;node->prev=NULL;return node;}
  • 这个函数没什么好说的,就是用malloc和sizeof来申请节点,如果申请成功就赋值,失败则exit。
  1. 初始化函数:
LTNode *LTInit()
{LTNode *phead= BuyLTNode(-1);phead->prev=phead;phead->next=phead;return phead;
}
  • 在这里插入图片描述
  1. 打印函数:

    void LTPrint(LTNode* phead)
    {assert(phead);printf("phead<->");LTNode *cur=phead->next;while(cur!=phead){printf("%d<->",cur->date);cur=cur->next;}printf("phead\n");
    }
    

在这里插入图片描述
4. 尾部插入函数:

void LTpushBack(LTNode *phead,LTDateType x)
{//判断phead是否为空指针assert(phead);//尾节点如下,无需再去遍历寻找尾节点LTNode *tail=phead->prev;//创建一个新节点LTNode *newnode=BuyLTNode(x);//改变指针指向,将尾节点与新节点相连newnode->prev=tail;tail->next=newnode;newnode->next=phead;phead->prev=newnode;}

在这里插入图片描述
5. 尾部删除函数

void LTpopBack(LTNode* phead)
{assert(phead);if (phead->next == phead)exit(-1);else{LTNode *tail=phead->prev;phead->prev=tail->prev;phead->prev->next=phead;free(tail);}
}

在这里插入图片描述
6. 头部插入函数

void LTpushFront(LTNode *phead,LTDateType x)
{assert(phead);
/*  LTNode *newnode= BuyLTNode(x);newnode->next=phead->next;phead->next->prev=newnode;phead->next=newnode;newnode->prev=phead;
*/LTNode *newnode= BuyLTNode(x);LTNode *first=phead->next;phead->next=newnode;newnode->prev=phead;newnode->next=first;first->prev=newnode;}

在这里插入图片描述
7. 头部删除函数

void LTpopFront(LTNode* phead)
{assert(phead);if (phead->next == phead)exit(-1);else{/* LTNode *first=phead->next;phead->next=first->next;phead->next->prev=phead;free(first);*/LTNode *first=phead->next;LTNode *second=first->next;free(first);phead->next=second;second->prev=phead;}}

在这里插入图片描述
8. 计算有效节点个数

int LTSize(LTNode* phead)
{assert(phead);int size=0;LTNode *cur=phead->next;while(cur!=phead){size++;cur=cur->next;}return size;
}

在这里插入图片描述

  • 补充一点就是,我这里是因为哨兵位的结构和链表节点的结构是一样的,是共用链表节点的结构的。也就是说,哨兵位里面的date的类型是和链表节点里存的数据类型是一样的,都是LTDataType。所以这里的如果要用哨兵位记录链表长度就只能是int类型,如果LTDataType变成了char类型这些,那么哨兵位里的data+1结果就会不如人意了,字符+1和int整型+1那肯定是不一样的结果嘛。
  • 但是如果你就要哨兵位记录链表长度,那就单独再创建一个哨兵位的结构,和链表节点的结构脱离,这个可以参考我讲队列的那篇文章。
  1. 查找数据的值为x的节点
LTNode *LTFind(LTNode *phead,LTDateType x)
{assert(phead);LTNode *cur=phead->next;while(cur!=phead){if(cur->date==x)return cur;cur=cur->next;}return NULL;}
  • 这里这个函数功能和上一个函数,也就是计算有效个数函数和打印函数实现逻辑类似,就不多说了。
  1. 在pos位置前插入值为x的节点
void LTInsert(LTNode* pos, LTDateType x)
{assert(pos);LTNode *posPrev=pos->prev;LTNode *newnode= BuyLTNode(x);posPrev->next=newnode;newnode->prev=posPrev;newnode->next=pos;pos->prev=newnode;}

在这里插入图片描述

  • 这个函数也可以复用在尾插和头插函数里
  1. 删除pos位置的节点

void LTErase(LTNode* pos)
{assert(pos);/*    pos->prev->next=pos->next;pos->next->prev=pos->prev;free(pos);*/LTNode *posPrev=pos->prev;LTNode *posNext=pos->next;free(pos);posPrev->next=posNext;posNext->prev=posPrev;}

在这里插入图片描述

  • 这个函数也可以复用在尾删和头删里
  1. 链表销毁函数
void LTDestroy(LTNode* phead)
{assert(phead);LTNode *cur=phead->next;while(cur!=phead){LTNode *next=cur->next;free(cur);cur=next;}}

在这里插入图片描述

  • 到此,链表的内容就全部讲完了。

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

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

相关文章

考研英语语法全攻略:从基础到长难句剖析​

引言 在考研英语的备考之旅中,语法犹如一座灯塔,为我们在浩瀚的英语知识海洋中指引方向。无论是阅读理解中复杂长难句的解读,还是写作时准确流畅表达的需求,扎实的语法基础都起着至关重要的作用。本文将结合有道考研语法基础入门课的相关内容,为大家全面梳理考研英语语法…

【计算机网络入门】应用层

目录 1.网络应用模型 1.1 C/S模型&#xff08;客户端服务器模型&#xff09; 1.2 P2P模型&#xff08;对等模型&#xff09; 2. DNS系统 2.1 域名 2.2 域名解析流程 3. FTP文件传输协议 4. 电子邮件系统 4.1 SMTP协议 4.2 pop3协议 4.3 IMAP协议 4.4 基于万维网的电…

【GoTeams】-3:构建api、重构错误码

本文目录 1. 构建api梳理调用关系api包的作用路由梳理注册Register代码语法 2. 重构错误码 1. 构建api 首先复制project-user&#xff0c;改名为project-api&#xff0c;放在总的路径下&#xff0c;然后在工作区中进行导入。 运行命令go work use .\project-api\新建工作区之…

游戏引擎学习第145天

仓库:https://gitee.com/mrxiao_com/2d_game_3 今天的计划 目前&#xff0c;我们正在完成遗留的工作。当时我们已经将声音混合器&#xff08;sound mixer&#xff09;集成到了 SIMD 中&#xff0c;但由于一个小插曲&#xff0c;没有及时完成循环内部的部分。这个小插曲主要是…

Qt 实现绘图板(支持橡皮擦与 Ctrl+Z 撤销功能)[特殊字符]

作业&#xff1a; 1&#xff1a;实现绘图的时候&#xff0c;颜色的随时调整 2&#xff1a;追加橡皮擦功能 3&#xff1a;配合键盘事件&#xff0c;实现功能 当键盘按 ctrlz的时候&#xff0c;撤销最后一次绘图 头文件.h #ifndef WIDGET_H #define WIDGET_H#include <QWidge…

打造智能聊天体验:前端集成 DeepSeek AI 助你快速上手

DeepSeek AI 聊天助手集成指南 先看完整效果&#xff1a; PixPin_2025-02-19_09-15-59 效果图&#xff1a; 目录 项目概述功能特点环境准备项目结构组件详解 ChatContainerChatInputMessageBubbleTypeWriter 核心代码示例使用指南常见问题 项目概述 基于 Vue 3 TypeScrip…

JPA编程,去重查询ES索引中的字段,对已有数据的去重过滤,而非全部字典数据

一、背景 课程管理界面&#xff0c;查询前&#xff0c;需要把查询元数据给出。 学科列表、学段列表和分类列表&#xff0c;我们把它定义为查询元数据。 一般的业务需求是&#xff1a; 系统维护好多个字典&#xff0c;比如学科、学段等等&#xff0c;相当于属性库。 但是&…

MySQL语法总结

本篇博客说明&#xff1a; &#xff01;&#xff01;&#xff01;.注意此系列都用的是MySQL语句&#xff0c;和SQLServer&#xff0c;PostgreSQL有些细节上的差别&#xff01;&#xff01;&#xff01; 1.每个操作都是先展示出语法格式 2.然后是具体例子 3.本篇注脚与文本顺讯息…

C++ Primer 交换操作

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

go切片定义和初始化

1.简介 切片是数组的一个引用&#xff0c;因此切片是引用类型&#xff0c;在进行传递时&#xff0c;遵守引用传递的机制。切片的使用和数组类似&#xff0c;遍历切片、访问切片的元素和切片的长度都一样。。切片的长度是可以变化的&#xff0c;因此切片是一个可以动态变化的数…

huggingface镜像站hf-mirror的各大AI模型文件下载

一、说明 huggingface地址&#xff1a;https://huggingface.co 但是由于需要国外网络&#xff0c;国内网络延迟较大或无法下载&#xff0c;所以使用国内镜像站&#xff1a; hf-mirror地址&#xff1a;https://hf-mirror.com/ 二、下载方法 1.本机安装了GIT 2.打开HF-Mirro…

Unity Shader 学习15:可交互式雪地流程

本质是 利用顶点变换实现的&#xff1a; 通过一个俯视整个场地的正交摄像机&#xff0c;根据绑定在移动物体身上的粒子系统&#xff0c;来获取物体移动过的位置&#xff0c;记录到一张RenderTexture上作为轨迹图&#xff0c;再通过这张图来对雪地做顶点变换。 1. 由于顶点变换需…

自由学习记录(42)

可能会出现到后面没有教程可以看&#xff0c;走不动&#xff0c;&#xff0c;但还是尝试吧 过程远比想象的要多 那连Live2d的这些脚本怎么控制的都要了解一下 ------------ 文件类型和扩展名 | 编辑手册 | Live2D Manuals & Tutorials 全部导入之后 在这下载SDK Live2D…

LeetCode - 28 找出字符串中第一个匹配项的下标

题目来源 28. 找出字符串中第一个匹配项的下标 - 力扣&#xff08;LeetCode&#xff09; 题目解析 暴力解法 本题如果采用暴力解法的话&#xff0c;可以定义两个指针 i&#xff0c;j&#xff0c;其中 i 指针用于扫描 S&#xff08;haystack&#xff09;串&#xff0c;j 指针…

windows下使用msys2编译ffmpeg

三种方法&#xff1a; 1、在msys2中使用gcc编译 2、在msys2中使用visual studio编译&#xff08;有环境变量&#xff09; 3、在msys2中使用visual studio编译&#xff08;无环境变量&#xff09; 我的环境&#xff1a; 1、msys2-x86_64-20250221 2、vs2015 3、ffmpeg-7.1…

【Python 数据结构 9.树】

我装作漠视一切&#xff0c;其实我在乎的太多&#xff0c;但我知道抓得越紧越容易失去 —— 25.3.6 一、树的基本概念 1.树的定义 树是n个结点的有限集合&#xff0c;n0时为空树。当n大于0的时候&#xff0c;满足如下两个条件&#xff1a; ① 有且仅有一个特定的结点&#xff…

数据结构--AVL树

一、二叉搜索树&#xff08;Binary Search Tree, BST&#xff09; 基本性质 对于树中的每个节点&#xff0c;其左子树中的所有节点值均小于该节点值。其右子树中的所有节点值均大于该节点值。左右子树也分别是二叉搜索树。 极端场景 在极端情况下&#xff0c;如插入节点顺序…

C语言——链表

大神文献&#xff1a;https://blog.csdn.net/weixin_73588765/article/details/128356985 目录 一、链表概念 1. 什么是链表&#xff1f; 1.1 链表的构成 2. 链表和数组的区别 数组的特点&#xff1a; 链表的特点&#xff1a; 二者对比&#xff1a; 二…

Qt:多线程

目录 初识Qt多线程 QThread常用API QThread的使用 Qt中的锁 条件变量和信号量 初识Qt多线程 Qt 多线程 和 Linux 中的线程本质是一个东西 Linux 中学过的 多线程 APl&#xff0c;Linux 系统提供的 pthread 库 Qt 中针对系统提供的线程 API 重新封装了 C11 中&#xff0c;…

如何用Kimi生成PPT?秒出PPT更高效!

做PPT是不是总是让你头疼&#xff1f;&#x1f629; 快速制作出专业的PPT&#xff0c;今天我们要推荐两款超级好用的AI工具——Kimi 和 秒出PPT&#xff01;我们来看看哪一款更适合你吧&#xff01;&#x1f680; &#x1f947; Kimi&#xff1a;让PPT制作更轻松 Kimi的生成效…