双向链表(数据结构与算法)

请添加图片描述

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿
🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟
🌟🌟 追风赶月莫停留 🌟🌟
🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀
🌟🌟 平芜尽处是春山🌟🌟
🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟
🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿🌿
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅

🍋双向链表

  • 🍌双向链表的定义与结构
  • 🍌双向链表增删查改(有头+双向+循环链表增删查改实现)
    • 🍍其它接口
    • 🍍创建返回链表的头结点
    • 🍍双向链表销毁
    • 🍍双向链表打印
    • 🍍双向链表尾插
    • 🍍双向链表尾删
    • 🍍双向链表头插
    • 🍍双向链表头删
    • 🍍双向链表查找
    • 🍍双向链表在pos的前面进行插入
    • 🍍双向链表删除pos位置的节点
  • 🍌双向链表整体代码的实现

🍌双向链表的定义与结构

在这里插入图片描述
双向链表:双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。(两个指针就是一个指向下一个数据,另一个指针指向上一个数据。如图中head头指针和tail尾指针)

在这里插入图片描述
在双向链表中还有一个特殊点,就是头指针,双向链表是带头的。在上次我们说的单链表中是不带头的,一旦涉及到头,我们就是先判断是不是为空,而在双向链表中就是不会涉及到这个问题了,不用再去一个一个的判断,就会省去很多的麻烦。

带头的双向链表嘴尾常用,所以我们这里就介绍带头的双向链表

🍌双向链表增删查改(有头+双向+循环链表增删查改实现)

🍍其它接口

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int LTdatetype;typedef struct ListNode
{LTdatetype date;struct ListNode* next;struct ListNode* tail;
}ListNode;

定义一些接口

🍍创建返回链表的头结点

//创建返回链表的头结点
ListNode* Create(LTdatetype x)
{ListNode* cur =(ListNode*)malloc(sizeof(ListNode));if (cur == NULL){perror("malloc  faild");exit(-1);}cur->next = NULL;cur->tail = NULL;cur->date = x;return cur;
}ListNode* Inia()
{ListNode* node = Create(0);node->next = node;node->tail = node;return node;
}

Create函数就是创建一个节点。而Inia函数本来是进行初始化,而想要变成循环的链表,形参改变实参那就要用到二级指针,因为这里是双向链表,涉及头指针的时候不需要考虑二级指针,所以在这里就只有初始化需要用到头指针,所以我们就干脆用返回值,这样就不看起来违和了,当然用二级指针也没有什么问题,看自己习惯即可。

🍍双向链表销毁

// 双向链表销毁
void Destreoy(ListNode* ps)
{assert(ps);ListNode* cur = ps->next;while (cur != ps){cur = cur->next;free(cur->tail);}free(ps);
}

🍍双向链表打印

// 双向链表打印
void Print(ListNode* ps)
{assert(ps);ListNode* cur = ps->next;while (cur != ps){printf("%d<=>", cur->date);cur = cur->next;}
}

在这里插入图片描述

在这里打印,我们就不能使用和单链表打印的方法了,因为我们这里是循环链表,并且我们的ps里面没有存有数据,所以我们要从ps的下个数据开始,也就是图中cur的位置,然后在cur到ps的时候跳出循环就可以了。

🍍双向链表尾插

// 双向链表尾插
void LTpushbake(ListNode* ps, LTdatetype x)
{assert(ps);ListNode* cur = Create(x);ListNode *node = ps->tail ;//ps的头要指向curnode->next = cur;//cur的尾要指向pscur->tail = node;//ps的尾要指向curps->tail = cur;//cur的头要指向pscur->next = ps;
}

在这里插入图片描述

之所以要这样的指向,是因为这是一个循环的双向链表

🍍双向链表尾删

// 双向链表尾删
void LTpopbake(ListNode* ps)
{assert(ps);assert(ps->next != ps);ListNode* cur = ps->tail;ListNode* curtail = cur->tail;free(cur);cur->next = cur->tail = NULL;curtail->next = ps;ps->tail = curtail;}

在这里插入图片描述
在这里因为这是循环链表所以不用遍历去找尾结点,当然用遍历也是可以的

🍍双向链表头插

// 双向链表头插
void LTpushfront(ListNode* ps, LTdatetype x)
{assert(ps);ListNode* node = Create(x);ListNode* cur = ps->next;ps->next = node;node->tail = ps;node->next = cur;cur->tail = node;}

在这里插入图片描述
带头的链表中,头插是最方便的,不用像不带头的单链表中一个一个去判断,大大的节省了我们的时间

🍍双向链表头删

// 双向链表头删
void LTpopfront(ListNode* ps)
{assert(ps);assert(ps->next != ps);ListNode* node = ps->next;ListNode* node_next = node->next;free(node);ps->next = node_next;node_next->tail = ps;
}

在这里插入图片描述
带头的链表中,头删也很方便了。

🍍双向链表查找

// 双向链表查找
ListNode * LTfind(ListNode* ps, LTdatetype x)
{assert(ps);ListNode* cur = ps->next;while (cur != ps){if (cur->date == x){return cur;}cur = cur->next;}return NULL;
}

关于这个查找还是要注意下,因为我们这里是循环链表,所以要注意记得要设置跳出循环的条件

🍍双向链表在pos的前面进行插入

// 双向链表在pos的前面进行插入
void LTinsert(ListNode *pos, LTdatetype x)
{assert(pos);ListNode* node = Create(x);ListNode* pos_tail = pos->tail;pos_tail->next = node;node->tail = pos_tail;node->next = pos;pos->tail = node;}

在这里插入图片描述
在pos前插入和头插,尾插差不多的意思

🍍双向链表删除pos位置的节点

// 双向链表删除pos位置的节点
void LTdelete(ListNode* pos)
{assert(pos);ListNode* pos_tail = pos->tail;ListNode* pos_next = pos->next;free(pos);pos_tail->next = pos_next;pos_next->tail = pos_tail;
}

在这里插入图片描述

删除pos位置,也差不多和前面意思都差不多

🍌双向链表整体代码的实现

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int LTdatetype;//创建返回链表的头结点
typedef struct ListNode
{LTdatetype date;struct ListNode* next;struct ListNode* tail;
}ListNode;ListNode* Create(LTdatetype x)
{ListNode* cur = (ListNode*)malloc(sizeof(ListNode));if (cur == NULL){perror("malloc  faild");exit(-1);}cur->next = NULL;cur->tail = NULL;cur->date = x;return cur;
}ListNode* Inia()
{ListNode* node = Create(0);node->next = node;node->tail = node;return node;
}// 双向链表销毁
void Destreoy(ListNode* ps)
{assert(ps);ListNode* cur = ps->next;while (cur != ps){cur = cur->next;free(cur->tail);}free(ps);
}// 双向链表打印
void LTprint(ListNode* ps)
{assert(ps);ListNode* cur = ps->next;printf("ps<=>");while (cur != ps){printf("%d<=>", cur->date);cur = cur->next;}printf("\n");
}// 双向链表尾插
void LTpushbake(ListNode* ps, LTdatetype x)
{assert(ps);ListNode* cur = Create(x);ListNode* node = ps->tail;node->next = cur;cur->tail = node;ps->tail = cur;cur->next = ps;
}// 双向链表尾删
void LTpopbake(ListNode* ps)
{assert(ps);assert(ps->next != ps);ListNode* cur = ps->tail;ListNode* curtail = cur->tail;free(cur);curtail->next = ps;ps->tail = curtail;}// 双向链表头插
void LTpushfront(ListNode* ps, LTdatetype x)
{assert(ps);ListNode* node = Create(x);ListNode* cur = ps->next;ps->next = node;node->tail = ps;node->next = cur;cur->tail = node;}// 双向链表头删
void LTpopfront(ListNode* ps)
{assert(ps);assert(ps->next != ps);ListNode* node = ps->next;ListNode* node_next = node->next;free(node);ps->next = node_next;node_next->tail = ps;
}// 双向链表查找
ListNode * LTfind(ListNode* ps, LTdatetype x)
{assert(ps);ListNode* cur = ps->next;while (cur != ps){if (cur->date == x){return cur;}cur = cur->next;}return NULL;
}// 双向链表在pos的前面进行插入
void LTinsert(ListNode *pos, LTdatetype x)
{assert(pos);ListNode* node = Create(x);ListNode* pos_tail = pos->tail;pos_tail->next = node;node->tail = pos_tail;node->next = pos;pos->tail = node;}// 双向链表删除pos位置的节点
void LTdelete(ListNode* pos)
{assert(pos);ListNode* pos_tail = pos->tail;ListNode* pos_next = pos->next;free(pos);pos_tail->next = pos_next;pos_next->tail = pos_tail;
}void test1()//测试
{ListNode* ps = Inia();LTpushbake(ps, 1);//尾插LTpushbake(ps, 2);//尾插LTpushbake(ps, 3);//尾插LTpushbake(ps, 4);//尾插LTprint(ps);//打印LTpopbake(ps);//尾删LTprint(ps);//打印LTpushfront(ps, 10);//头插LTpushfront(ps, 11);//头插LTpushfront(ps, 12);//头插LTpushfront(ps, 13);//头插LTprint(ps);//打印LTpopfront(ps);//头删LTprint(ps);//打印ListNode *pos = LTfind(ps, 11);//双向链表查找if (pos == NULL){printf("没找到\n");}else{printf("找到了,为:%d\n", pos->date);}LTinsert(pos, 100);// 双向链表在pos的前面进行插入LTprint(ps);//打印LTdelete(pos);//双向链表删除pos位置的节点LTprint(ps);//打印Destreoy(ps);//双向链表销毁
}int main()
{test1();//测试return 0;
}

带头的双向链表实现起来还是非常简单的,大家如果对于文章中的某一个点有问题,欢迎私聊我。

请添加图片描述

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

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

相关文章

【论文极速读】LVM,视觉大模型的GPT时刻?

【论文极速读】LVM&#xff0c;视觉大模型的GPT时刻&#xff1f; FesianXu 20231210 at Baidu Search Team 前言 这一周&#xff0c;LVM在arxiv上刚挂出不久&#xff0c;就被众多自媒体宣传为『视觉大模型的GPT时刻』&#xff0c;笔者抱着强烈的好奇心&#xff0c;在繁忙工作之…

开源组件与中间件的学习笔记: C++, linux, git

文章目录 C入门基本内容 linux系统与基本命令总体认知基本内容 开发工具和git基本内容 感言一些感悟 C入门 基本内容 小非是刚入职的员工&#xff0c; 在熟悉完git和vscode之后就开始了写代码 &#xff0c;但是老张不放心&#xff0c;担心小飞写出屎山代码&#xff0c; 想要看…

财务机器人(RPA)会影响会计人员从业吗?

财务机器人会对会计从业人员有影响。 不过是正面积极的影响。 它是财务人员工作的好助手好帮手。 具体展开聊聊财务RPA机器人是如何成为财务人员的好帮手。 财务机器人是在人工智能和自动化技术的基础上建立的、以软件机器人作为虚拟劳动力、依据预先设定的程序与现有用户系…

代码随想录二刷 |二叉树 |94.二叉树的中序遍历

代码随想录二刷 &#xff5c;二叉树 &#xff5c;二叉树的中序遍历 题目描述解题思路代码实现迭代法递归法 题目描述 94.二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&a…

prompt工程

微信公众号转载&#xff0c;关注微信公众号掌握更多技术动态 --------------------------------------------------------------- 一、prompt基础 提示包括传递给语言模型的指令和语境&#xff0c;以实现预期的任务。提示工程是开发和优化提示的实践&#xff0c;以便在各种应用…

Nacos 配置中心源码 | 京东物流技术团队

客户端 入口 在引入配置中心 maven 依赖的 jar 文件中找到 spring-cloud-starter-alibaba-nacos-config-2.2.5.RELEASE.jar!/META-INF/spring.factories&#xff0c;在该配置文件找到 NacosConfigBootstrapConfiguration 配置类&#xff0c;该类是 nacos 配置中心的入口类&am…

linux 14网站架构 编译安装mysql数据库

目录 LNMP网站架构下载源码包mysql 下载位置 mysql 安装1.1、清理安装环境&#xff1a;1.2、创建mysql用户1.3、从官网下载tar包1.4、安装编译工具1.5、解压1.6、编译安装编译安装三部曲1.7、初始化初始化,只需要初始化一次1.8、启动mysql1.9、登录mysql1.10、systemctl启动方式…

Kafka事务是怎么实现的?Kafka事务消息原理详解

目录 一、Kafka事务性消息1.1 介绍Kafka事务性消息1.2 事务性消息的应用场景1.3 Kafka事务性消息的优势 二、Kafka事务性消息的使用2.1 配置Kafka以支持事务性消息生产者配置消费者配置 2.2 生产者&#xff1a;发送事务性消息创建Kafka生产者开始事务发送消息提交或中止事务 2.…

CESM笔记——component活动状态+compset前缀解析+B1850,BHIST区别

时隔一年没写CSDN笔记了&#xff0c;一些CESM的知识点我都快忘了。诶&#xff0c;主要是在国外办公室的网屏蔽了好多国内的网络&#xff0c;CSDN登不上&#xff0c;回家又不想干活。。。好吧&#xff0c;好多借口。。。 昨天师弟问我一些问题&#xff0c;想想要不可以水一篇小…

31条PCB设计布线技巧:

大家在做PCB设计时&#xff0c;都会发现布线这个环节必不可少&#xff0c;而且布线的合理性&#xff0c;也决定了PCB的美观度和其生产成本的高低&#xff0c;同时还能体现出电路性能和散热性能的好坏&#xff0c;以及是否可以让器件的性能达到最优等。 本篇内容&#xff0c;将…

向量数据库:AIGC时代的必备基础工具

今天分享的AIGC系列深度研究报告&#xff1a;《向量数据库&#xff1a;AIGC时代的必备基础工具》。 &#xff08;报告出品方&#xff1a;广发证券&#xff09; 报告共计&#xff1a;47页 点击添加图片描述&#xff08;最多60个字&#xff09;编辑 一、向量数据库为 AI 大模型…

jmeter 压测需要的部分配置

修改jmeter 目录的bin目录下的jmeter.properties文件 解除KeepAlive设置 修改接口的高级中的实现和超时 解除httpclient4.retrycount前的注释符并将0修改为1 即修改为&#xff1a;httpclient4.retrycount1 解除httpclient4.idletimeout前的注释符并修改为合适间隔 即修改为…