【数据结构】——双链表的实现(赋源码)

双链表的概念和结构

双链表的全称叫做:带头双向循环链表

它的结构示意图如下

注意:这⾥的“带头”跟前⾯我们说的单链表的“头结点”是两个概念,实际前⾯的在单链表阶段称呼不严谨,但是为了读者们更好的理解就直接称为单链表的头结点。 

带头链表⾥的头结点,实际为“哨兵位”,哨兵位结点不存储任何有效元素,只是站在这⾥“放哨的”也可以认为是用来占位置滴!!!

双链表的实现

首先先在结构体当中输入需要的数据,则有如下的数据是需要的

结构体中的数据

typedef int LTDataType;//方便对数据类型进行统一的替换
typedef struct ListNode ListNode;//对结构体的名称重新命名交ListNode
struct ListNode
{LTDataType data;ListNode* next;ListNode* prev;
};

则上面的图可以变成这样

双链表新结点的创建及双链表的初始化

ListNode* LTBuyNode(LTDataType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));//一个结构体的大小if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = newnode->prev = newnode;return newnode;//返回头结点
}

链表的初始化需要一个创建的新的结点作为哨兵位

//void LTInit(ListNode** pphead)
//{
//	//创建一个头结点即“哨兵位”
//	*pphead = LTBuyNode(-1);
//}ListNode* LTInit()
{ListNode* phead = LTBuyNode(-1);return phead;
}//上面是两种初始化的方法
//第一种需要传递一个二级指针

在上面的代码当中,我们只需要创建一个头结点来保证第一个“头”存在即可。

插入
第一个参数传一级还是二级,要看phead指向的结点会不会改变
如果发生改变,那么pphead的改变要影响实参,传二级
如果不发生改变,pphead不会影响实参,传一级

双链表的尾插

//尾插
void LTPushBack(ListNode* phead, LTDataType x)
{assert(phead);//创建需要插入的结点//上面初始化的newnode是头结点,这个newnode是尾插的结点ListNode* newnode = LTBuyNode(x);newnode->next = phead;newnode->prev = phead->prev;phead->prev->next = newnode;phead->prev = newnode;
}

 上面的顺序是不能改变的,否则无法让新结点找到原来链表的位置

这边测试一下我们的尾插代码依次插入1 2 3 4  

 双链表的头插

//头插
void LTPushFront(ListNode* phead, LTDataType x)
{assert(phead);ListNode* newnode = LTBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}

头插和尾插是类似的 ,不过有一个特殊的地方

头插是头插在哨兵位和第一个真正的结点中间

同样的,上面的顺序位置是不能改变的

测试头插代码

这个代码是在上面尾插代码基础上的操作

 双链表的尾删

//尾删
void LTPopBack(ListNode* phead)
{assert(phead);assert(!Empty(phead));ListNode* del = phead->prev;ListNode* prev = del->prev;prev->next = phead;phead->prev = prev;free(del);del = NULL;
}

 这边仍然是在尾插的基础上的操作

这边我们进行了五次尾删,所以代码assert断言了!

将一次尾删注释,下面就是尾删的效果 

双链表的头删 

//头删
void LTPopFront(ListNode* phead)
{assert(phead);assert(!Empty(phead));ListNode* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}

这个仍然是在尾插的基础上操作的,如果继续删除,跟上面的情况一样assert断言报错 

以上就是最基础的增删 ,下面开始加大难度!

双链表中查找数据pos

//找相同数据
ListNode* LTFind(ListNode* phead, LTDataType x)
{assert(phead);ListNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

这边我们查找的是数据3,所以我们可以找到 

 

这个在链表中没有数据6,所以我们没有找到相关的数据 

 在pos之后插入结点

这个和尾插以及头插其实是类似的,这里主要是寻找到pos结点,然后插入想要的数据

//在pos之后插入结点
void LTInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = LTBuyNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}

在3的后面插入数据10 

 

删除pos结点 

​//删除指定位置节点
void LTErase(ListNode* pos)
{assert(pos);// pos->prev  pos   pos->nextpos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}​

双链表的销毁 

这里我们提供了两种的销毁方法,两种方法基本是类似的

//销毁
void LTDesTroy(ListNode** pphead)
{assert(pphead && *pphead);ListNode* pcur = (*pphead)->next;while (pcur != *pphead){ListNode* next = pcur->next;free(pcur);pcur = next;}//销毁哨兵位结点free(*pphead);*pphead = NULL;pcur = NULL;
}
//为了更好的记忆,我们让销毁也传递一级指针
void LTDesTroy2(ListNode* phead)//传一级,需要手动将plist置为NULL
{assert(phead);ListNode* pcur = phead->next;while (pcur != phead){ListNode* next = pcur->next;free(pcur);pcur = next;}free(phead);phead = pcur = NULL;
}

最后我们将双向链表的源码附上

list.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int LTDataType;
typedef struct ListNode ListNode;
struct ListNode
{LTDataType data;ListNode* next;ListNode* prev;
};ListNode* LTBuyNode(LTDataType x);
//为了保存接口的一致性
//
//初始化
//void LTInit(ListNode** pphead);
ListNode* LTInit();  //
void LTPrint(ListNode* phead);bool Empty(ListNode* phead);//插入
//第一个参数传一级还是二级,要看phead指向的结点会不会改变
//如果发生改变,那么pphead的改变要影响实参,传二级
//如果不发生改变,pphead不会影响实参,传一级//尾插
void LTPushBack(ListNode* phead, LTDataType x);//头插
void LTPushFront(ListNode* phead, LTDataType x);//尾删
void LTPopBack(ListNode* phead);//头删
void LTPopFront(ListNode* phead);//在pos之后插入结点
void LTInsert(ListNode* pos, LTDataType x);//删除指定位置的结点
void LTErase(ListNode* pos);//找数据
ListNode* LTFind(ListNode* phead, LTDataType x);//销毁
void LTDesTroy(ListNode** pphead);
void LTDesTroy2(ListNode* phead);//传一级,需要手动将plist置为NULL

 List.c

#include"List.h"ListNode* LTBuyNode(LTDataType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = newnode->prev = newnode;return newnode;
}
//初始化//void LTInit(ListNode** pphead)
//{
//	//创建一个头结点即“哨兵位”
//	*pphead = LTBuyNode(-1);
//}
ListNode* LTInit()
{ListNode* phead = LTBuyNode(-1);return phead;
}//打印
void LTPrint(ListNode* phead)
{ListNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}bool Empty(ListNode* phead)
{assert(phead);return phead->next == phead;
}//尾插
void LTPushBack(ListNode* phead, LTDataType x)
{assert(phead);//创建需要插入的结点ListNode* newnode = LTBuyNode(x);newnode->next = phead;newnode->prev = phead->prev;phead->prev->next = newnode;phead->prev = newnode;
}
//头插
void LTPushFront(ListNode* phead, LTDataType x)
{assert(phead);ListNode* newnode = LTBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}//尾删
void LTPopBack(ListNode* phead)
{assert(phead);assert(!Empty(phead));ListNode* del = phead->prev;ListNode* prev = del->prev;prev->next = phead;phead->prev = prev;free(del);del = NULL;
}//头删
void LTPopFront(ListNode* phead)
{assert(phead);assert(!Empty(phead));ListNode* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}
//找相同数据
ListNode* LTFind(ListNode* phead, LTDataType x)
{assert(phead);ListNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}//在pos之后插入结点
void LTInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = LTBuyNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}//删除指定位置节点
void LTErase(ListNode* pos)
{assert(pos);// pos->prev  pos   pos->nextpos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}//销毁
void LTDesTroy(ListNode** pphead)
{assert(pphead && *pphead);ListNode* pcur = (*pphead)->next;while (pcur != *pphead){ListNode* next = pcur->next;free(pcur);pcur = next;}//销毁哨兵位结点free(*pphead);*pphead = NULL;pcur = NULL;
}void LTDesTroy2(ListNode* phead)
{assert(phead);ListNode* pcur = phead->next;while (pcur != phead){ListNode* next = pcur->next;free(pcur);pcur = next;}free(phead);phead = pcur = NULL;
}

test.c 

#include"List.h"
void test()
{创建双向链表变量//ListNode* plist = NULL;//LTInit(&plist);ListNode* plist = LTInit();LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);LTPrint(plist);ListNode* pos = LTFind(plist, 3);LTInsert(pos, 10);LTPrint(plist);pos = LTFind(plist, 3);LTErase(pos);LTPrint(plist);/*LTPopFront(plist);LTPrint(plist);LTPopFront(plist);LTPrint(plist);LTPopFront(plist);LTPrint(plist);LTPopFront(plist);LTPrint(plist);*//*LTPushFront(plist, 1);LTPrint(plist);LTPushFront(plist, 2);LTPrint(plist);LTPushFront(plist, 3);LTPrint(plist);LTPushFront(plist, 4);LTPrint(plist);*//*LTPopBack(plist);LTPrint(plist);LTPopBack(plist);LTPrint(plist);LTPopBack(plist);LTPrint(plist);LTPopBack(plist);LTPrint(plist);LTPopBack(plist);LTPrint(plist);*//*if (pos == NULL){printf("没有找到\n");}else{printf("找到了\n");}*/LTDesTroy(&plist);//LTDesTroy2(plist);//plist = NULL;}int main()
{test();return 0;
}

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

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

相关文章

Transformer——逐步详解架构和完整代码搭建

好久没更新博客&#xff0c;后面更新会勤一些。今天想聊一下Transformer&#xff0c;Transformer在NLP和CV领域都有着重要的价值&#xff0c;甚至可以看作是一个基础模型&#xff0c;这篇博客将通过详细代码深入解析Transformer模型总体架构图各个部分的的作用和搭建:论文链接&…

遥感领域新方向!Mamba+RS论文汇总!

本文总结了将Mamba应用至遥感领域的相关论文&#xff08;14篇&#xff09;&#xff0c;涉及到的论文见文末链接&#xff0c;具体如下&#xff1a; 文章目录 1. 遥感图像处理2. 多/高光谱图像分类3. 变化检测/语义分割4. 遥感图像融合/超分辨率 1. 遥感图像处理 论文题目&#…

6.3 面向对象技术-设计模式

设计模式 创建型模式 结构型模式 行为型模式 真题

配置本地开发服务器代理请求以及登录模块开发(二)

项目初始化完成之后&#xff0c;准备开始进行项目的开发&#xff0c;首先配置好开发环境作为整个项目的基础 一、配置代理 1、config/proxy.ts配置代理 export default {// 如果需要自定义本地开发服务器 请取消注释按需调整dev: {// localhost:8000/api/** -> https://p…

第07课 Scratch入门篇:水果音乐钢琴

水果音乐钢琴 入门篇适合新手&#xff0c;如您已经学过&#xff0c;可以忽略本节课&#xff01; 一、故事背景&#xff1a; 在一个充满创意和想象的奇妙世界里&#xff0c;有一架与众不同的钢琴——水果音乐钢琴。这架钢琴的键盘不是由普通的黑白键组成&#xff0c;而是由各种…

http post请求 - 最简测试环境 - 使用flask

1.缘起 工作中&#xff0c;我们有时需要测试web post功能是否正常。这类测试&#xff0c;客户端的请求很容易实现&#xff0c;比如portman&#xff0c;比如非常简单的命令行curl语法&#xff1a; curl -X POST http://127.0.0.1:5000/post-endpoint/ -F "warning_image/p…

鸿蒙 HarmonyOS NEXT端云一体化开发-云数据库篇

一、概述 云数据库是一款基于对象模型的数据库&#xff0c;采用存储区、对象类型和对象三级结构。 数据模型 存储区 存储区是一个独立的数据存储区域&#xff0c;多个数据存储区之间相互独立&#xff0c;每个存储区拥有完全相同的对象类型定义 --类似于关系型数据库中的da…

一番赏小程序开发,为消费者带来更多新鲜体验

一番赏作为经典的潮玩方式&#xff0c;深受消费者的喜爱&#xff0c;一番赏还会与不同的热门IP合作&#xff0c;不断推出新的赏品&#xff0c;吸引众多粉丝。赏品的内容非常丰富&#xff0c;从手办、公仔玩具等&#xff0c;还设有隐藏款和最终赏商品&#xff0c;对玩家拥有着非…

哲学CSSCI南大核心期刊论文投稿推荐(含发表方向)

发表哲学方向的C刊论文&#xff0c;却在选刊上一直找不到合适的。本文介绍14本哲学CSSCI南大核心期刊名单&#xff0c;帮助您快速找到哲学类期刊&#xff01; 哲学类CSSCI核心期刊推荐&#xff1a; 1、逻辑学研究 发表内容方向&#xff1a;符号逻辑、非形式逻辑、逻辑与哲学、…

Synchronized的锁升级过程是怎样的?

文章目录 一、Synchronized的使用1、修饰实例方法2、修饰静态方法3、修饰代码块4、总结&#xff1a; 二、Monitor1、Java对象头1.1 32 位虚拟机的对象头1.2 64位虚拟机的对象头 2、Mark Word 结构3、Moniter4、Synchronized 字节码5、轻量级锁6、锁膨胀7、自旋优化8、偏向锁9、…

命令行使用ADB,不用root,完美卸载小米预装软件

ADB安装与运行 install java 下载安装 注意选择JDK17以上版本 https://www.oracle.com/java/technologies/downloads/#jdk22-windows 选择中间的安装文件下载 编辑系统变量 C:\Program Files (x86)\Java\jdk-22 C:\Program Files (x86)\Java\jdk-22\bin 把C:\Progra…

K210视觉识别模块学习笔记7:多线程多模型编程识别

今日开始学习K210视觉识别模块: 图形化操作函数 亚博智能 K210视觉识别模块...... 固件库: canmv_yahboom_v2.1.1.bin 训练网站: 嘉楠开发者社区 今日学习使用多线程、多模型来识别各种物体 这里先提前说一下本文这次测试实验的结果吧&#xff1a;结果是不太成…

视频去水印免费电脑版 pdf压缩在线免费网页版 pdf压缩在线免费 简单工具软件详细方法步骤分享

消除视频中的恼人水印&#xff0c;是许多视频编辑爱好者的常见需求。在这篇文章中&#xff0c;我们将探讨几种视频去水印的技巧&#xff0c;在数字化时代&#xff0c;视频和图片的传播越来越方便&#xff0c;但随之而来的水印问题也让人头疼。本文将为您详细介绍视频剪辑去水印…

捕获会自动消失的消息提示弹窗

如上图&#xff0c;我们会在一些场景碰到会自动消失的消息提示弹窗&#xff0c;一般存在个3-5秒&#xff0c;我们在做UI断言时&#xff0c;需要监测这个弹窗是否会出现&#xff0c;就需要去捕获这个弹窗的位置 我们打开浏览器的开发者模式(F12)&#xff0c;找到源码(Sources) …

探索 Redis 不同集群架构的性能与应用

1. 引言 Redis的集群配置成为了提高数据可靠性和服务可用性的关键。本文将带领大家了解Redis的四种主要集群架构&#xff0c;并重点分析哨兵模式和Redis Cluster架构和优势。 2. Redis的四种集群架构 2.1 单实例Redis 使用单个 Redis 实例提供服务。适用于小规模应用&#…

MiniExcel:.NET中处理Excel的高效方案

在.NET开发环境中&#xff0c;处理Excel文件是一项常见的任务&#xff0c;无论是数据导入、导出还是报表生成。传统的解决方案可能存在性能瓶颈或功能限制。MiniExcel作为一个现代、高效的库&#xff0c;为.NET开发者提供了一个强大的工具来简化Excel操作。本文将介绍MiniExcel…

爬虫程序在采集亚马逊站点数据时如何绕过验证码限制?

引言 在电商数据分析中&#xff0c;爬虫技术的应用日益广泛。通过爬虫技术&#xff0c;我们可以高效地获取大量的电商平台数据&#xff0c;这些数据对于市场分析、竞争情报、价格监控等有着极其重要的意义。亚马逊作为全球最大的电商平台之一&#xff0c;是数据采集的重要目标…

【技术升级】Docker环境下Nacos平滑升级攻略,安全配置一步到位

目前项目当中使用的Nacos版本为2.0.2&#xff0c;该版本可能存在一定的安全风险。软件的安全性是一个持续关注的问题&#xff0c;尤其是对于像Nacos这样的服务发现与配置管理平台&#xff0c;它在微服务架构中扮演着核心角色。随着新版本的发布&#xff0c;开发团队会修复已知的…

【解决】ubuntu20.04 root用户无法SSH登陆问题

Ubuntu root用户无法登录的问题通常可以通过修改‌SSH配置文件和系统登录配置来解决。 修改SSH配置文件 sudo vim /etc/ssh/sshd_config 找到 PermitRootLogin 设置&#xff0c;并将其值更改为 yes 以允许root用户通过SSH登录 保存并关闭文件之后&#xff0c;需要重启SSH服务…

【HarmonyOS】实现矩形上下拖动、动态拖拽修改高度

简介 实现一个矩形块上下拖动&#xff0c;并且可以拖动边缘定位点改变矩形块高度。实现效果如下&#xff1a; 代码 Entry Component struct Rec_Page {State penOffsetY: number 0;State offsetX: number 0State offsetY: number 0State positionX: number 0State posi…