当前位置: 首页 > news >正文

数据结构---双链表

专栏:数据结构
个人主页:HaiFan.
专栏简介:从零开始,数据结构!!

双链表

  • 前言
  • 双链表各接口的实现
  • 为要插入的值开辟一块空间BuyLN
  • 初始化LNInit和销毁LNDestory
  • 打印链表中的值LNPrint
  • 尾插LNPushBack和尾删LNPopBack
  • 头插LNPushFron和头删LNPopFront
  • 判断链表是否为空LNEmpty
  • 查找LNFind
  • 任意位置插入 LNInsert
  • 任意位置删除LNErase
  • 测试结果
  • 源代码

前言

在这里插入图片描述

双向链表是在结构体内存两个指针,一个指针存下一个结点的地址,另一个指针是存上一个结点的地址,这样,就可以通过一个结点,找到前后两个结点。听起来很复杂,但实现起来,要比单链表简单许多。

在这里插入图片描述

双链表:

typedef int LDataType;typedef struct ListNode
{LDataType val;struct ListNode* head;struct ListNode* tail;
}LN;

双链表各接口的实现

LN* BuyLN(LDataType x);//为要插入的值开辟一块空间void LNInit(LN** pphead);//初始化
void LNDestory(LN* phead);//销毁void LNPrint(LN* phead);//打印链表中的值void LNPushBack(LN* phead, LDataType x);//尾插
void LNPopBack(LN* phead);//尾删void LNPushFront(LN* phead, LDataType x);//头插
void LNPopFront(LN* phead);//头删bool LNEmpty(LN* phead);//双链表是否为空LN* LNFind(LN* phead, LDataType x);//查找void LNInsert(LN* pos, LDataType x);//任意位置插入
void LNErase(LN* pos);//任意位置删除

为要插入的值开辟一块空间BuyLN

这个BuyLN是为链表开辟一块空间,为什么要额外写一个这函数,因为在插入值的过程中,需要为这个值开辟一块空间,每次都需要写相同的代码,所以把这个代码写成一个函数,用到式直接调用函数即可

LN* BuyLN(LDataType x)
{LN* sn = (LN*)malloc(sizeof(LN));if (sn == NULL){perror("malloc fail");exit(-1);}sn->val = x;sn->head = NULL;sn->tail = NULL;return sn;
}

初始化LNInit和销毁LNDestory

双链表的初始化,只需要把链表中的head和tail指针都指向自己,形成一个环即可。

void LNInit(LN** pphead)
{(*pphead) = BuyLN(-1);(*pphead)->head = *pphead;(*pphead)->tail = *pphead;
}

在这里插入图片描述


空间有开辟就有销毁,销毁之后把空间还给系统

void LNDestory(LN* phead)//ٿռ
{assert(phead);LN* cur = phead->head;while (cur != phead){LN* sn = cur;free(sn);sn = NULL;cur = cur->head;}
}

打印链表中的值LNPrint

头结点是不需要打印的,所以可以先定义一个sn代表头结点指向的下一个元素的位置,然后依次打印,直到sn的地址和头节点的地址相同时,打印结束

void LNPrint(LN* phead)
{assert(phead);LN* sn = phead->head;while (sn != phead){cout << sn->val << ' ';sn = sn->head;}cout << endl;
}

尾插LNPushBack和尾删LNPopBack

单链表的尾插需要先遍历,找到尾,然后进行插入操作,双链表不同于单链表,双链表有两个指针,一个存下一个结点,一个存上一个结点,我们只需要在头节点的左面,也就是让头节点中存上一个结点存要插入的值即可,这样就能完成尾插。

在这里插入图片描述

这样就不需要在遍历链表,找尾了。

void LNPushBack(LN* phead, LDataType x)
{LN* newnode = BuyLN(x);LN* prev = phead->tail;prev->head = newnode;newnode->tail = prev;newnode->head = phead;phead->tail = newnode;
}

尾删依旧是把头节点左边的那一个元素给删除即可,当然,表中如果只有头节点,就不要在删除了。。。

void LNPopBack(LN* phead)
{assert(phead->head != phead->tail);LN* prev = phead->tail;prev->tail->head = phead;phead->tail = prev->tail;free(prev);prev = NULL;
}

头插LNPushFron和头删LNPopFront

在这里插入图片描述

头插的话,要先找到首元素,然后把要插入的元素和头节点,首元素都双向链接起来即可。

void LNPushFront(LN* phead, LDataType x)
{LN* newnode = BuyLN(x);LN* ne = phead->head;ne->tail = newnode;newnode->head = ne;phead->head = newnode;newnode->tail = phead;
}

头删的话呢,跟上面一样,要先找到首元素,然后让头节点和首元素指向的下一个元素链接起来即可。

void LNPopFront(LN* phead)
{assert(phead->head != phead->tail);LN* ne = phead->head;phead->head = ne->head;ne->head->tail = phead;free(ne);ne = NULL;}

在这里插入图片描述

判断链表是否为空LNEmpty

如果链表为空返回true,反之,返回false,当双链表的头指针和尾指针都指向自己的时候,就说明,双链表为空。

bool LNEmpty(LN* phead)
{assert(phead);return phead->head == phead->tail;
}

查找LNFind

查找一个元素,返回这个元素的地址,只需要遍历双链表即可。

LN* LNFind(LN* phead, LDataType x)
{assert(phead);assert(phead->head != phead->tail);LN* cur = phead->head;while (cur != phead){if (x == cur->val){return cur;}cur = cur->head;}return NULL;
}

任意位置插入 LNInsert

再插入之前,要先找到要插入的位置,寻找这个位置的任务就交给了上面写的查找函数,我们只需要传递参数,对该位置进行插入即可,

void LNInsert(LN* pos, LDataType x)
{assert(pos);LN* newnode = BuyLN(x);LN* prev = pos->head;pos->head = newnode;newnode->tail = pos;newnode->head = prev;prev->tail = newnode;
}

任意位置删除LNErase

和上面一样,要删除哪个元素,就要先找到那个元素,然后进行删除操作,找到那个元素的任务依旧交给LNFind函数

void LNErase(LN* pos)
{assert(pos);LN* ne = pos->head;pos->head = ne->head;ne->tail = pos;
}

测试结果

测试代码:

#include "List.h"void TestLN()
{LN* plist = NULL;LNInit(&plist);LNPrint(plist);LNPushBack(plist, 1);LNPushBack(plist, 2);LNPushBack(plist, 3);LNPushBack(plist, 4);LNPushBack(plist, 5);LNPrint(plist);LNPopBack(plist);LNPrint(plist);LNPushFront(plist, -1);LNPushFront(plist, -2);LNPushFront(plist, -3);LNPushFront(plist, -4);LNPushFront(plist, -5);LNPrint(plist);LNPopFront(plist);LNPrint(plist);LN* ret = LNFind(plist, 3);if (ret != NULL){cout << ret->val << ' ' << ret << endl;LNInsert(ret, 10000);LNErase(ret);}LNPrint(plist);LNDestory(plist);
}int main()
{TestLN();return 0;
}

在这里插入图片描述

源代码

.h文件

#pragma once#include <iostream>
#include <assert.h>
#include <stdlib.h>using namespace std;typedef int LDataType;typedef struct ListNode
{LDataType val;struct ListNode* head;struct ListNode* tail;
}LN;LN* BuyLN(LDataType x);//为要插入的值开辟一块空间void LNInit(LN** pphead);//初始化
void LNDestory(LN* phead);//销毁void LNPrint(LN* phead);//打印链表中的值void LNPushBack(LN* phead, LDataType x);//尾插
void LNPopBack(LN* phead);//尾删void LNPushFront(LN* phead, LDataType x);//头插
void LNPopFront(LN* phead);//头删bool LNEmpty(LN* phead);//双链表是否为空LN* LNFind(LN* phead, LDataType x);//查找void LNInsert(LN* pos, LDataType x);//任意位置插入
void LNErase(LN* pos);//任意位置删除

.cpp文件

#include "List.h"LN* BuyLN(LDataType x)
{LN* sn = (LN*)malloc(sizeof(LN));if (sn == NULL){perror("malloc fail");exit(-1);}sn->val = x;sn->head = NULL;sn->tail = NULL;return sn;
}void LNPrint(LN* phead)
{assert(phead);LN* sn = phead->head;while (sn != phead){cout << sn->val << ' ';sn = sn->head;}cout << endl;
}void LNInit(LN** pphead)//ʼ
{(*pphead) = BuyLN(-1);(*pphead)->head = *pphead;(*pphead)->tail = *pphead;
}void LNDestory(LN* phead)//ٿռ
{assert(phead);LN* cur = phead->head;while (cur != phead){LN* sn = cur;free(sn);sn = NULL;cur = cur->head;}
}void LNPushBack(LN* phead, LDataType x)
{LN* newnode = BuyLN(x);LN* prev = phead->tail;prev->head = newnode;newnode->tail = prev;newnode->head = phead;phead->tail = newnode;
}void LNPopBack(LN* phead)
{assert(phead->head != phead->tail);LN* prev = phead->tail;prev->tail->head = phead;phead->tail = prev->tail;free(prev);prev = NULL;
}void LNPushFront(LN* phead, LDataType x)
{LN* newnode = BuyLN(x);LN* ne = phead->head;ne->tail = newnode;newnode->head = ne;phead->head = newnode;newnode->tail = phead;
}void LNPopFront(LN* phead)
{assert(phead->head != phead->tail);LN* ne = phead->head;phead->head = ne->head;ne->head->tail = phead;free(ne);ne = NULL;}bool LNEmpty(LN* phead)
{assert(phead);return phead->head == phead->tail;
}LN* LNFind(LN* phead, LDataType x)
{assert(phead);assert(phead->head != phead->tail);LN* cur = phead->head;while (cur != phead){if (x == cur->val){return cur;}cur = cur->head;}return NULL;
}void LNInsert(LN* pos, LDataType x)
{assert(pos);LN* newnode = BuyLN(x);LN* prev = pos->head;pos->head = newnode;newnode->tail = pos;newnode->head = prev;prev->tail = newnode;
}void LNErase(LN* pos)
{assert(pos);LN* ne = pos->head;pos->head = ne->head;ne->tail = pos;
}

test.cpp文件

#define _CRT_SECURE_NO_WARNINGS 1#include "List.h"void TestLN()
{LN* plist = NULL;LNInit(&plist);LNPrint(plist);LNPushBack(plist, 1);LNPushBack(plist, 2);LNPushBack(plist, 3);LNPushBack(plist, 4);LNPushBack(plist, 5);LNPrint(plist);LNPopBack(plist);LNPrint(plist);LNPushFront(plist, -1);LNPushFront(plist, -2);LNPushFront(plist, -3);LNPushFront(plist, -4);LNPushFront(plist, -5);LNPrint(plist);LNPopFront(plist);LNPrint(plist);LN* ret = LNFind(plist, 3);if (ret != NULL){cout << ret->val << ' ' << ret << endl;LNInsert(ret, 10000);LNErase(ret);}LNPrint(plist);LNDestory(plist);
}int main()
{TestLN();return 0;
}

相关文章:

数据结构---双链表

专栏&#xff1a;数据结构 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;从零开始&#xff0c;数据结构&#xff01;&#xff01; 双链表前言双链表各接口的实现为要插入的值开辟一块空间BuyLN初始化LNInit和销毁LNDestory打印链表中的值LNPrint尾插LNPushBack和尾删LNPop…...

Windows 环境安装Scala详情

为了进一步学习Spark&#xff0c;必须先学习Scala 编程语言。首先开始Scala 环境搭建。温馨提示&#xff1a;本文是基于Windows 11 安装Scala 2.13.1 版本第一步&#xff1a;确保本机已经正确安装JDK1.8 环境第二步&#xff1a;Scala 官网下载我们所属scala版本文件。Scala 官网…...

C++ Qt自建网页浏览器

C Qt自建网页浏览器如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01;前言这篇博客针对<<C Qt自建网页浏览器>>编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应用推荐首选。文…...

Flink从入门到精通系列(四)

5、DataStream API&#xff08;基础篇&#xff09; Flink 有非常灵活的分层 API 设计&#xff0c;其中的核心层就是 DataStream/DataSet API。由于新版本已经实现了流批一体&#xff0c;DataSet API 将被弃用&#xff0c;官方推荐统一使用 DataStream API 处理流数据和批数据。…...

Nginx 配置实例-反向代理案例一

实现效果&#xff1a;使用nginx反向代理&#xff0c;访问 www.suke.com 直接跳转到本机地址127.0.0.1:8080 一、准备工作 Centos7 安装 Nginxhttps://liush.blog.csdn.net/article/details/125027693 1. 启动一个 tomcat Centos7安装JDK1.8https://liush.blog.csdn.net/arti…...

为什么北欧的顶级程序员数量远超中国?

说起北欧&#xff0c;很多人会想到寒冷的冬天&#xff0c;漫长的极夜&#xff0c;童话王国和圣诞老人&#xff0c;但是如果我罗列下诞生于北欧的计算机技术&#xff0c;恐怕你会惊掉下巴。Linux&#xff1a;世界上最流行的开源操作系统&#xff0c;最早的内核由Linus Torvalds开…...

vuex getters的作用和使用(求平均年龄),以及辅助函数mapGetters

getters作用&#xff1a;派生状态数据mapGetters作用&#xff1a;映射getters中的数据使用&#xff1a;方法名自定义&#xff0c;系统自动注入参数&#xff1a;state&#xff0c;每一个方法中必须有return&#xff0c;其return的结果被该方法名所接收。在state中声明数据listst…...

20230311给Ubuntu18.04下的GTX1080M安装驱动

20230311给Ubuntu18.04下的GTX1080M安装驱动 2023/3/11 12:50 2. 安装GTX1080驱动 安装 Nvidia 驱动 367.27 sudo add-apt-repository ppa:graphics-drivers/ppa 第一次运行出现如下的警告&#xff1a; Fresh drivers from upstream, currently shipping Nvidia. ## Curren…...

2023腾讯面试真题:

​【腾讯】面试真题&#xff1a; 1、Kafka 是什么&#xff1f;主要应用场景有哪些&#xff1f; Kafka 是一个分布式流式处理平台。这到底是什么意思呢&#xff1f; 流平台具有三个关键功能&#xff1a; 消息队列&#xff1a;发布和订阅消息流&#xff0c;这个功能类似于消息…...

23种设计模式-建造者模式(Android应用场景介绍)

什么是建造者模式 建造者模式是一种创建型设计模式&#xff0c;它允许您使用相同的创建过程来生成不同类型和表示的对象。在本文中&#xff0c;我们将深入探讨建造者模式的Java实现&#xff0c;并通过一个例子来解释其工作原理。我们还将探讨如何在Android应用程序中使用建造者…...

English Learning - L2 语音作业打卡 双元音 [ʊə] [eə] Day17 2023.3.9 周四

English Learning - L2 语音作业打卡 双元音 [ʊə] [eə] Day17 2023.3.9 周四&#x1f48c;发音小贴士&#xff1a;&#x1f48c;当日目标音发音规则/技巧:&#x1f36d; Part 1【热身练习】&#x1f36d; Part2【练习内容】&#x1f36d;【练习感受】&#x1f353;元音 [ʊə…...

【动态规划】多重背包问题,分组背包问题

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…...

JAVA面向对象特征之——封装

4.封装 private关键字 是一个权限修饰符 可以修饰成员(成员变量和成员方法) 作用是保护成员不被别的类使用&#xff0c;被private修饰的成员只在本类中才能访问 针对private修饰的成员变量&#xff0c;如果需要被其他类使用&#xff0c;提供相应的操作 提供 “get变量名()…...

【数据结构】二叉树相关OJ题

文章目录一、单值二叉树二、检查两颗树是否相同三、判断一棵树是否为另一颗树的子树四、对称二叉树五、二叉树的前序遍历六、二叉树中序遍历七、二叉树的后序遍历八、二叉树的构建及遍历一、单值二叉树 单值二叉树 题目描述 如果二叉树每个节点都具有相同的值&#xff0c;那…...

Windows安装Hadoop

当初搭建Hadoop、Hive、HBase、Flink等这些没有截图写文&#xff0c;今为分享特重装。下载Hadoop下载地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/以管理员身份运行cmd切换到所在目录执行start winrar x -y hadoop-3.3.4.tar.gz&#xff0c;解压。配置…...

ICG-Hydrazide,吲哚菁绿-酰肼,ICG-HZ结构式,溶于二氯甲烷等部分有机溶剂,

ICG-Hydrazide,吲哚菁绿-酰肼 中文名称&#xff1a;吲哚菁绿-酰肼 英文名称&#xff1a;ICG-Hydrazide 英文别名&#xff1a;ICG-HZ 性状&#xff1a;粉末或固体 溶剂&#xff1a;溶于二氯甲烷等部分有机溶剂 稳定性&#xff1a;-20℃密封保存、置阴凉干燥处、防潮 分子…...

【论文阅读】浏览器扩展危害-Helping or Hindering? How Browser Extensions Undermine Security

本文来源于ACM CCS 2022&#xff1b; https://dl.acm.org/doi/10.1145/3548606.3560685 摘要 “浏览器扩展”是轻量级的浏览器附加组件&#xff0c;使用各个浏览器特定的功能丰富的JavaScript api&#xff0c;为用户提供了额外的Web客户端功能&#xff0c;如改进网站外观和与…...

线性和非线性最小二乘问题的常见解法总结

线性和非线性最小二乘问题的各种解法 先看这篇博客&#xff0c;非常好&#xff1a;线性和非线性最小二乘问题的各种解法 1. 线性最小二乘问题有最优解 但是面对大型稀疏矩阵的时候使用迭代法效率更好。 迭代法 有Jacobi迭代法、 Seidel迭代法及Sor法 【数值分析】Jacobi、Se…...

数据库知识点

数据库是指按照一定规则存储、组织和管理数据的系统。在现代化的信息化社会中&#xff0c;数据库已经成为了各种应用系统中不可或缺的一部分。因此&#xff0c;对于数据库的知识掌握不仅是计算机专业人员必备的技能&#xff0c;也是各个行业从业者必须具备的基本素质之一。 数…...

Maven打包构建Docker镜像并推送到仓库

Maven打包构建Docker镜像并推送到仓库 文章目录Maven打包构建Docker镜像并推送到仓库一&#xff0c;服务器Docker配置二&#xff0c;本地项目maven配置2.1 pom.xml2.2 dockerfile2.3 验证2.4 统一dockerfile对于开发完成的服务要发布至服务器Docker时&#xff0c;我刚学习了解D…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...