4.双向循环链表的模拟实现

1.双向链表的实现

image-20240404225836268

1.1双向链表节点的结构声明

typedef int LTDataType;typedef struct ListNode
{struct ListNode* prev;  // 指向该节点的前一个节点struct ListNode* next;  // 指向该节点的后一个节点LTDataType data;        // 该节点中存储的数据
}LTNode;					// 将这个结构体重新命名为LTNode

1.2链表的初始化

image-20221221050208838

LTNode* ListInit()
{// 创建一个节点,这个节点是哨兵位LTNode* guard = (LTNode*)malloc(sizeof(LTNode));if (guard == NULL){perror("malloc fail");exit(-1);}// 这是一个带头双向循环的链表// 此时只有一个哨兵位,因此哨兵位的前一个节点和后一个节点都是哨兵位本身guard->next = guard;guard->prev = guard;return guard;
}

1.3 链表的尾插

// 创建一个新节点
LTNode* BuyListNode(LTDataType x)
{// 创建一个新节点LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL){perror("malloc fail");exit(-1);}// 初始化这个节点的指针,和数据node->next = NULL;node->prev = NULL;node->data = x;return node;
}

image-20221221045924349

// 尾插
void ListPushBack(LTNode* phead, LTDataType x)
{assert(phead);// 尾插一个数据前,创建一个新节点,将这个数据存储到这个节点中LTNode* newnode = BuyListNode(x);// phead就是头节点,因为是双向循环链表,所以phead->prev就是双向循环链表的尾节点LTNode* tail = phead->prev;    // 尾插一个新节点,就是将下面三个节点,按照顺序进行连接// tail newnode phead // 1.尾节点指向新节点tail->next = newnode; // 2.新节点的前指针,指向尾节点newnode->prev = tail;// 3.新节点的后指针,指向头节点newnode->next = phead;// 4.头节点的前指针,指向新节点,新节点成为这个双向循环链表的新的尾节点phead->prev = newnode;
}

1.4链表的打印

image-20220828163401093

void ListPrint(LTNode* phead)
{assert(phead);printf("phead<=>");//不打印哨兵位的头// phead就是哨兵位,不存储数据,因此不需要进行打印LTNode* cur = phead->next;  // cur等于phead,说明已经循环了一周,所有的数据都已经被打印了while (cur != phead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");
}

1.5链表的头插

  • 方法一

image-20220828164424170

  • 方法二

image-20220828164452671

// 因为双向循环链表是有哨兵位的,头插的节点是插入到哨兵位之后的
// 也就是将 head newnode d1 这三个节点,按照顺序连接就可以
void ListPushFront(LTNode* phead, LTDataType x)
{assert(phead);// 方法一:// 需要关心节点连接的循序(如果先执行第二步,那么phead指向的就是新节点,而不是d1节点)// 创建一个要插入的新节点,存储的数据为xLTNode* newnode = BuyListNode(x);// 第一步// phead->next就是d1节点// 新节点的后指针指向d1节点newnode->next = phead->next;// phead->next->prev 就是d1节点的前指针;也就是d1节点的前指针指向新节点phead->next->prev = newnode;// 第二步// 哨兵位的后指针指向新节点phead->next = newnode;// 新节点的前指针指向哨兵位newnode->prev = phead;// 方法二:/*// 不关心顺序(第一步和第二步哪个先执行都可以)LTNode* newnode = BuyListNode(x);// 1.先保存d1节点的地址LTNode* first = phead->next;// 第一步:// 哨兵位的后指针指向新节点phead->next = newnode;// 新节点的前指针指向哨兵位newnode->prev = phead;// 第二步:// 新节点的后指针指向d1节点newnode->next = first;// d1节点的前指针指向新节点first->prev = newnode;*/// 方法三:// 在phead->next位置前插入,也就是头插// void ListInsert(LTNode* pos, LTDataType x)  这个函数就是在pos位置前插入一个节点// ListInsert(phead->next, x);
}

1.6链表的尾删

image-20240405171132075

// 用来判断是否为空链表,空链表无法头删
bool ListEmpty(LTNode* phead)
{assert(phead);// 方法一(比较麻烦)/*if (phead->next == phead)return true;elsereturn false;*/// 如果相等,那么就是空链表(因为这是一个双向循环链表)return phead->next == phead;
}
void ListPopBack(LTNode* phead)
{assert(phead);assert(!ListEmpty(phead));// 哨兵位的前指针指向的就是双向循环链表的尾节点LTNode* tail = phead->prev;// 尾节点的前指针指向的前节点将其命名为prevLTNode* prev = tail->prev;// 要进行尾删,也就是将 phead tail prev 三个节点中的tail删除,再将phead和prev连接// 1.连接phead和prevprev->next = phead;phead->prev = prev;// 释放要删除的节点空间,将其变为空指针,防止产生野指针free(tail);tail = NULL;
}

1.7链表的头删

image-20220828170122719

// 对于双向循环链表,头删就是将 phead d1 d2中的d1节点删除,再将phead和d2节点连接
void ListPopFront(LTNode* phead)
{assert(phead);// 保证这个双向循环链表不是空链表assert(!ListEmpty(phead));// phead->next就是d1节点LTNode* first = phead->next;// phead->next->next就是d2节点LTNode* second = first->next;// 连接phead和d2节点phead->next = second;second->prev = phead;// 释放d1节点free(first);first = NULL;
}

1.8链表节点的数量

size_t ListSize(LTNode* phead)
{assert(phead);size_t n = 0;LTNode* cur = phead->next;// 当cur != phead,说明链表循环一周结束while (cur != phead){++n;cur = cur->next;}return n;
}

1.9链表的查找

LTNode* ListFind(LTNode* phead, LTDataType x)
{assert(phead);size_t n = 0;// 哨兵位没有存储数据,不需要进行查找LTNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}// 迭代cur = cur->next;}return NULL;
}

1.10在pos位置之前插入

image-20220828172755297

// 在pos之前插入
// 也就是将prev newnode pos进行连接
void ListInsert(LTNode* pos, LTDataType x)
{assert(pos);// 将pos前指针指向的节点,命名为prevLTNode* prev = pos->prev;// 创建一个新节点LTNode* newnode = BuyListNode(x);// 连接3个节点 prev newnode pos;prev->next = newnode;newnode->prev = prev;newnode->next = pos;pos->prev = newnode;
}

头插

void ListPushFront(LTNode* phead, LTDataType x)
{assert(phead);//在phead->next之前插入,也就是头插ListInsert(phead->next, x);
}

尾插

image-20220828173223249

void ListPushBack(LTNode* phead, LTDataType x)
{assert(phead);//在phead之前插入,也就是尾插ListInsert(phead, x);
}

1.11删除pos位置

image-20220828173538770

void ListErase(LTNode* pos)
{assert(pos);// prev pos nextLTNode* prev = pos->prev;LTNode* next = pos->next;// 连接prev和next,释放pos节点prev->next = next;next->prev = prev;free(pos);//需要调用的人自行置空,因为pos是传值拷贝,在这里将pos置空,主函数中的pos并没有被改变//pos = NULL; 
}

尾删

void ListPopBack(LTNode* phead)
{assert(phead);assert(!ListEmpty(phead));//删除phead->prev也就是尾删ListErase(phead->prev); 
}

头删

void ListPopFront(LTNode* phead)
{assert(phead);assert(!ListEmpty(phead));// 删除phead->next也就是头删ListErase(phead->next);
}

1.12销毁链表


void ListDestory(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){LTNode* next = cur->next;// 释放当前节点free(cur);// 迭代cur = next;}// 释放哨兵位free(phead);// 可以传二级,内部置空头结点// 建议:也可以考虑用一级指针,让调用ListDestory的人置空  (保持接口一致性)// phead = NULL;
}

2.双向循环链表的完整实现

  • List.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>// 双链表的节点的结构体
typedef int LTDataType;
typedef struct ListNode
{struct ListNode* next;struct ListNode* prev;LTDataType data;
}LTNode;// 初始化一个双向循环链表
LTNode* ListInit();
// 销毁这个双向循环链表
void ListDestory(LTNode* phead);// 打印链表
void ListPrint(LTNode* phead);// 尾插
void ListPushBack(LTNode* phead, LTDataType x);// 头插
void ListPushFront(LTNode* phead, LTDataType x);// 尾删
void ListPopBack(LTNode* phead);// 头删
void ListPopFront(LTNode* phead);// 判断链表是否为空
bool ListEmpty(LTNode* phead);// 链表的节点数量
size_t ListSize(LTNode* phead);// 查找指定节点
LTNode* ListFind(LTNode* phead, LTDataType x);// pos位置前插入节点
void ListInsert(LTNode* pos, LTDataType x);
// 删除pos位置的节点
void ListErase(LTNode* pos);
  • List.c
#include "List.h"// 初始化,并返回哨兵位的地址
LTNode* ListInit()
{LTNode* guard = (LTNode*)malloc(sizeof(LTNode));if (guard == NULL){perror("malloc fail");exit(-1);}guard->next = guard;guard->prev = guard;return guard;
}// 创建一个新节点
LTNode* BuyListNode(LTDataType x)
{LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL){perror("malloc fail");exit(-1);}node->next = NULL;node->prev = NULL;node->data = x;return node;
}// 打印链表中的数据
void ListPrint(LTNode* phead)
{assert(phead);printf("phead<=>");LTNode* cur = phead->next;while (cur != phead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");
}void ListPushBack(LTNode* phead, LTDataType x)
{assert(phead);ListInsert(phead, x);
}void ListPushFront(LTNode* phead, LTDataType x)
{assert(phead);ListInsert(phead->next, x);
}void ListPopBack(LTNode* phead)
{assert(phead);assert(!ListEmpty(phead));ListErase(phead->prev);
}void ListPopFront(LTNode* phead)
{assert(phead);assert(!ListEmpty(phead));ListErase(phead->next);
}bool ListEmpty(LTNode* phead)
{assert(phead);return phead->next == phead;
}size_t ListSize(LTNode* phead)
{assert(phead);size_t n = 0;LTNode* cur = phead->next;while (cur != phead){++n;cur = cur->next;}return n;
}LTNode* ListFind(LTNode* phead, LTDataType x)
{assert(phead);size_t n = 0;LTNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}// 在pos之前插入
void ListInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* prev = pos->prev;LTNode* newnode = BuyListNode(x);// prev newnode pos;prev->next = newnode;newnode->prev = prev;newnode->next = pos;pos->prev = newnode;
}// 删除pos位置
void ListErase(LTNode* pos)
{assert(pos);LTNode* prev = pos->prev;LTNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);//pos = NULL;
}// 可以传二级,内部置空头结点
// 建议:也可以考虑用一级指针,让调用ListDestory的人置空  (保持接口一致性)
void ListDestory(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){LTNode* next = cur->next;free(cur);cur = next;}free(phead);
}
  • main.c
#define _CRT_SECURE_NO_WARNINGS#include "List.h"void TestList1()
{// 初始化链表LTNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPrint(plist);ListPushFront(plist, 10);ListPushFront(plist, 20);ListPushFront(plist, 30);ListPushFront(plist, 40);ListPrint(plist);ListPopBack(plist);ListPopBack(plist);ListPopBack(plist);ListPopBack(plist);ListPrint(plist);ListPopBack(plist);ListPopBack(plist);ListPopBack(plist);ListPopBack(plist);ListPrint(plist);// 防止内存泄漏ListDestory(plist);plist = NULL;
}void TestList2()
{LTNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPrint(plist);ListPopFront(plist);ListPopFront(plist);ListPrint(plist);ListPopFront(plist);ListPopFront(plist);ListPrint(plist);ListDestory(plist);plist = NULL;
}void TestList3()
{LTNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPrint(plist);// ...
}int main()
{TestList2();return 0;
}

3.顺序表和链表的区别

image-20220828180116805

image-20220828180145437
image-20220828180204839

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

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

相关文章

Linux内核errno-base.h源码分析

上次写过一个博客&#xff0c;主要关于内核错误相关的源码分析&#xff08;链接&#xff09;&#xff0c;最近突然发现上次的分析不完善&#xff0c;因此本次完善相关分析。 Linux内核中经常见到一些返回值&#xff0c;如-12&#xff0c;比如下面是我遇到过的一个截图&#xff…

【Java面试题】MySQL上篇(索引)

文章目录 索引1.索引的分类&#xff1f;2.B树和B树的区别&#xff1f;2.1B树2.2B树 3.为什么使用索引会加快查询&#xff1f;4.创建索引的注意点&#xff1f;5.索引在哪些情况下会失效&#xff1f;6.聚簇索引和非聚簇索引的区别&#xff1f;7.回表查询是什么&#xff1f;8.什么…

阿里云租用服务器GPU配置报价单_1年_一个月_1小时价格表

阿里云GPU服务器租用价格表包括包年包月价格、一个小时收费以及学生GPU服务器租用费用&#xff0c;阿里云GPU计算卡包括NVIDIA V100计算卡、T4计算卡、A10计算卡和A100计算卡&#xff0c;GPU云服务器gn6i可享受3折优惠&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云GPU…

电脑磁盘空间不足?学会这几招,轻松释放磁盘空间

随着科技的飞速发展&#xff0c;电脑已成为我们日常生活中不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;我们都需要依赖电脑来完成。然而&#xff0c;随着电脑使用时间的增长&#xff0c;磁盘空间不足的问题也逐渐浮现。当磁盘空间不足时&#xff0c;不仅会影响电脑…

2023年金融贷款骗局套路之一

源地址&#xff1a;2023年金融贷款骗局套路之一_预防网贷套路_计算机技术网 随着无卡消费的日夜流行&#xff0c;三年疫情出现&#xff0c;钱难寻&#xff0c;难找的尴尬境地&#xff0c;贷款骗局也出现不少。今天我们讲讲最近很流行的贷款骗局之一中的一种贷款骗局。 在平常…

第40篇:有限状态机<三>

Q&#xff1a;本期我们介绍有限状态机的应用之一&#xff1a;摩尔状态机“1101”序列检测器。 A&#xff1a;当检测到序列1101时&#xff0c;状态机输出为1。定义s_0为初始状态&#xff08;即没有检测到1输入的状态&#xff09;&#xff0c;摩尔状态机的输出仅取决于现态&…

基于SpringBoot+Vue的个性化推荐电商平台(源码+文档+部署+讲解)

一.系统概述 随着网络科技的不断发展以及人们经济水平的逐步提高&#xff0c;网络技术如今已成为人们生活中不可缺少的一部分&#xff0c;而信息管理系统是通过计算机技术&#xff0c;针对用户需求开发与设计&#xff0c;该技术尤其在各行业领域发挥了巨大的作用&#xff0c;有…

数学建模-Matlab中randperm函数及其双重进阶版

1.randperm函数的用法 &#xff08;1&#xff09;这种用法就是参数只有一个数字&#xff0c;代表的含义就是随机排列之后打印输出&#xff1b; 我们举例的数字是4&#xff0c;就会把1到4这4个数字随机打乱之后随机输出&#xff0c;每次运行结果都不一样 所有可能的情况是n的…

UI自动化测试案例

备注:本文为博主原创文章,未经博主允许禁止转载。如有问题,欢迎指正。 个人笔记(整理不易,有帮助,收藏+点赞+评论,爱你们!!!你的支持是我写作的动力) 笔记目录:笔记本~笔记目录_airtest和selenium那个好用-CSDN博客 个人随笔:工作总结随笔_8、以前工作中都接触过哪…

鸿蒙南向开发:【智能烟感】

样例简介 智能烟感系统通过实时监测环境中烟雾浓度&#xff0c;当烟雾浓度超标时&#xff0c;及时向用户发出警报。在连接网络后&#xff0c;配合数字管家应用&#xff0c;用户可以远程配置智能烟感系统的报警阈值&#xff0c;远程接收智能烟感系统报警信息。实现对危险及时报…

Java区域基层卫生云联his系统源码 医院信息管理系统源码

基于云计算的医疗卫生信息系统(cloud-based healthcare informationsystem&#xff0c;简称“云 HIS”)是基于云计算技术&#xff0c;将医院信息系统、电子病历系统、实验室信息系统、公共卫生等系统横向集成的系统&#xff0c;为医疗机构信息化建设提供标准化、信息化、协同化…

003Node.js创建第一个web服务

如果用PHP来编写后端代码&#xff0c;需要用Apache或者Nginx的服务器,来处理客户的请求响应。对于Node.js时&#xff0c;不仅实现了应用&#xff0c;同时还实现了整个HTTP服务器. 安装 Node Snippets插件&#xff08;编程自带提示&#xff09; console.log(你好nodejs); //表…

代理模式:控制对象访问的智能方式

在面向对象的软件开发中&#xff0c;代理模式是一种结构型设计模式&#xff0c;它为其他对象提供一个代理或占位符以控制对这个对象的访问。代理模式在实现权限控制、延迟初始化和远程对象访问等方面非常有用。本文将详细介绍代理模式的定义、实现、应用场景以及优缺点&#xf…

【进阶篇】二、实现Java Agent的静态加载和动态加载

文章目录 1、Java Agent2、两种加载模式静态加载模式动态加载模式 3、静态加载模式实现4、动态加载的实现 1、Java Agent 通过Java Agent&#xff0c;生成一种特殊的jar包&#xff08;一种工具&#xff09;&#xff0c;业务程序可以主动去调用jar包里的方法。比如下面这个有打…

奎芯科技:智能时代的芯片上游企业如何突破?

半导体IP&#xff08;Intellectual Property&#xff0c;知识产权&#xff09;&#xff0c;通常也称作IP核&#xff08;IP core&#xff09;&#xff0c;指芯片设计中预先设计、验证好的功能模块&#xff0c;主要服务于芯片设计&#xff0c;因部分通用功能模块在芯片中被反复使…

Linux磁盘扩容并设置挂载点

背景 使用pve创建了一个虚拟机&#xff0c;各种环境配置都安装好了之后发现分配的磁盘空间太小了&#xff0c;默认的就30多个G&#xff0c;这还没咋玩呢就满了&#xff0c;像扩容却找遍了这个pve都没找到扩容按钮&#xff0c;并且我这个磁盘不是lvm结构的&#xff0c;所以好像…

工业网络互联互通问题的探讨——青创智通

工业物联网解决方案-工业IOT-青创智通 随着科技的不断发展&#xff0c;工业网络已经成为现代工业生产中不可或缺的一部分。工业网络能够将各个生产设备、传感器、执行器等连接在一起&#xff0c;实现信息的共享和协同工作&#xff0c;从而提高生产效率、降低能耗、提升产品质量…

局域网内部使用的视频会议系统推荐

随着远程办公的普及和全球化的发展趋势&#xff0c;企业需要一个高效、灵活、安全的音视频会议解决方案&#xff0c;以支持远程办公的协同工作、跨地域沟通等需要。私有化音视频会议就是一个适合企业自身部署的解决方案。它不仅能够满足企业信息管理和保密的需求&#xff0c;而…

广告电商模式:购物得积分 看广告还能赚钱!

互联网时代的浪潮中&#xff0c;电子商务与广告行业日新月异&#xff0c;不断碰撞出全新的火花。在这种背景下&#xff0c;广告电商模式作为一种创新的商业模式崭露头角&#xff0c;它以广告与电商的深度融合为核心&#xff0c;通过精准化的营销手段与用户的积极参与&#xff0…