链表之双向链表的实现
铁汁们大家好,我们上一篇博客学习了单链表,这节课让我们继续往深学习,学习一下双线链表,话不多说,我们开始吧!
目录
1.双向链表
2.顺序表和链表的优缺点
3.双向链表的实现
1.双向链表
1.我们要实现的双线链表是带头双向循环链表,它的结构最复杂,一般用在单独的存储数据。我们实际中使用的链表数据结构都是带头双向循环链表。
2.它虽然结构复杂,但是在我们用代码实现过程中,它比单链表简单。
3.相信很多铁汁不清楚双向链表的结构是什么,如下图:
2.顺序表和链表的优缺点
我们在这里总结一下这两种线性表,方便之后的学习。
顺序表:
优点:空间连续,支持随机访问
缺点:中间或前面部分的插入和删除,时间复杂度是O(n);
增容很不方便,代价较大。
链表:
优点:任意位置的插入删除,时间复杂度为O(1);
没有增容销耗,按需申请节点空间,不用了直接释放。
缺点:以节点为单位存储,不支持随机访问
3.双向链表的实现
经过上面的铺垫,我们来实现一个带头双向循环链表
List.h文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int ADataType;
typedef struct ListNode
{ADataType data;struct ListNode* prev;//双线链表前驱指针struct ListNode* next;//后继指针
}LN;//双向链表初始化
LN* ListInit();
//为节点开辟空间
LN* BuyListCapacity(ADataType x);
//链表的销毁
void ListDestory(LN* phead);
//头插
void ListPushFront(LN* phead, ADataType x);
//尾插
void ListPushBack(LN* phead, ADataType x);
//打印
void ListPrint(LN* phead);
//头删
void ListPopFront(LN* phead);
//尾删
void ListPopBack(LN* phead);
//查找链表中的数据
LN* ListSearch(LN* phead, ADataType x);
//修改找到的数据
void ListModify( LN* pos, ADataType y);
//在这个位置后插入数据
void ListInsert(LN* pos, ADataType x);
//删除这个位置之后的数据
void ListErase(LN* pos);
//判断链表是否为空
bool ListEmpty(LN* phead);
List.c文件
#include"List.h"//为节点开辟空间
LN* BuyListCapacity(ADataType x)
{LN* newnode = (LN*)malloc(sizeof(LN));if (newnode == NULL){perror("malloc is false!\n");exit(-1);}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}//双向链表初始化
LN* ListInit()
{//开辟空间LN* phead = BuyListCapacity(0);//让头节点的前驱和后继都指向自己,是一个循环phead->next = phead;phead->prev = phead;return phead;
}//链表的销毁
void ListDestory(LN* phead)
{assert(phead);LN* tail = phead->next;while (tail != phead)//链表的头尾相连,当尾等于头时,说明链表空了{LN* next = tail->next;free(tail);tail = next;}free(phead);phead = NULL;
}
//头插
void ListPushFront(LN* phead, ADataType x)
{assert(phead);LN* newnode = BuyListCapacity(x);//若这里不使用新的变量来存储原来第一个节点的值,就先链接后,在链接前newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;newnode->prev = phead;
}
//尾插
void ListPushBack(LN* phead, ADataType x)
{assert(phead);LN* newnode = BuyListCapacity(x);//找到尾,进行插入节点LN* tail = phead->prev;tail->next = newnode;newnode->prev = tail;phead->prev = newnode;newnode->next = phead;
}
//打印
void ListPrint(LN* phead)
{assert(phead);LN* tail = phead->next;while (tail != phead){printf(" %d ", tail->data);tail = tail->next;}printf("\n");
}
//头删
void ListPopFront(LN* phead)
{assert(phead);//判断链表是否为空,为空则删不了if (phead->next == phead){printf("List is NULL!\n");return;}//先记录下后一个节点LN* first = phead->next;LN* second = first->next;phead->next = second;second->prev = phead;free(first);first = NULL;
}
//尾删
void ListPopBack(LN* phead)
{assert(phead);//判断链表是否为空if (phead->prev == phead){printf("List is NULL!\n");return;}LN* tail = phead->prev;LN* prev = tail->prev;prev->next = phead;phead->prev = prev;free(tail);tail = NULL;
}
//查找链表中的数据
LN* ListSearch(LN* phead, ADataType x)
{assert(phead);LN* cur = phead->next;while (cur->data != x){cur = cur->next;}if (cur->data == x){return cur;}return NULL;
}
//修改找到的数据
void ListModify( LN* pos, ADataType y)
{assert(pos);pos->data = y;
}
//在这个位置后插入数据
void ListInsert(LN* pos, ADataType x)
{assert(pos);LN* newnode = BuyListCapacity(x);LN* next = pos->next;pos->next = newnode;newnode->prev = pos;newnode->next = next;next->prev = newnode;
}
//删除这个位置之后的数据
void ListErase(LN* pos)
{assert(pos);LN* cur = pos->next;LN* next = cur->next;pos->next = next;next->prev = pos;free(cur);cur = NULL;
}
//判断链表是否为空
bool ListEmpty(LN* phead)
{assert(phead);if (phead->prev == phead || phead->next == phead){return true;}return false;
}
Test.c文件
#include"List.h"
//带头双向循环链表的实现void Test1()
{LN* head = ListInit();ListPushFront(head, 33);ListPushFront(head, 22);ListPushFront(head, 11);ListPushBack(head, 4);ListPushBack(head, 5);ListPushBack(head, 6);ListPushBack(head, 7);ListPushBack(head, 8);ListPushBack(head, 9);ListPushBack(head, 10);printf("ListNode:> ");ListPrint(head);ListPopFront(head);ListPopBack(head);printf("ListNode:> ");ListPrint(head);LN* pos = ListSearch(head, 7);if (pos == NULL){printf("Not Find!\n");}else{printf("the number is %d\n", pos->data);ListModify(pos, 77);printf("ListNode:> ");ListPrint(head);ListInsert(pos, 13);ListInsert(pos, 14);ListInsert(pos, 15);printf("ListNode:> ");ListPrint(head);ListErase(pos);printf("ListNode:> ");ListPrint(head);}if (ListEmpty(head)){printf("List is NULL!\n");}else{printf("List is Notnull!\n");}ListDestory(head);printf("List is disory!\n");
}int main()
{Test1();return 0;
}
结果:结果就是这样的,大家可以自己尝试一下!
这就是双向链表的实现,大家还是要自己敲一遍代码,帮助自己更好的掌握知识点。
谢谢铁汁们的支持,咱们下期再见!!!
相关文章:
链表之双向链表的实现
铁汁们大家好,我们上一篇博客学习了单链表,这节课让我们继续往深学习,学习一下双线链表,话不多说,我们开始吧! 目录 1.双向链表 2.顺序表和链表的优缺点 3.双向链表的实现 1.双向链表 1.我们要实现的双线…...
小白学大模型:什么是生成式人工智能?
什么是生成式人工智能? 在过去几年中,机器学习领域取得了迅猛进步,创造了人工智能的一个新的子领域:生成式人工智能。这些程序通过分析大量的数字化材料产生新颖的文本、图像、音乐和软件,我将这些程序简称为“GAIs”…...
并发编程01-深入理解Java并发/线程等待/通知机制
为什么我们要学习并发编程? 最直白的原因,因为面试需要,我们来看看美团和阿里对 Java 岗位的 JD: 从上面两大互联网公司的招聘需求可以看到, 大厂的 Java 岗的并发编程能力属于标配。 而在非大厂的公司, 并…...
3.类与对象(中篇)介绍了类的6个默认构造函数,列举了相关案例,实现了一个日期类
1.类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。 默认成员函数:用户没有显式实现,编译器会…...
Vue实现手机APP页面的切换,如何使用Vue Router进行路由管理呢?
在Vue中,实现手机APP页面的切换,通常会使用Vue Router进行路由管理。Vue Router是Vue.js官方的路由管理器,它和Vue.js深度集成,使构建单页面应用变得易如反掌。 以下是一个简单的步骤说明,展示如何使用Vue Router实现…...
软考--软件设计师(软件工程总结2)
目录 1.测试方法 2.软件项目管理 3.软件容错技术 4.软件复杂性度量 5.结构化分析方法(一种面向数据流的开发方法) 6.数据流图 1.测试方法 软件测试:静态测试(被测程序采用人工检测,计算机辅助静态分析的手段&…...
渗透测试之SSRF漏洞
一、SSRF介绍 SSRF(Cross-site Scripting,简称XSS)是一种安全漏洞,它允许攻击者通过构造特定的请求,使服务器发起对外网无法访问的内部系统请求。这种漏洞通常发生在服务端提供了从其他服务器应用获取数据的功能&#…...
【C++】1957. 求三个数的平均数
问题:1957. 求三个数的平均数 类型:基本运算、小数运算 题目描述: 小雅刚刚考完语文、数学、英语的三门期中考试,她想请你编个程序来帮她算算她的平均分。 要求输入三个正整数,分别表示三科考试的分数,输…...
GPU部署ChatGLM3
首先,检查一下自己的电脑有没有CUDA环境,没有的话,去安装一个。我的电脑是4060显卡,买回来就自带这些环境了。没有显卡的话,也不要紧,这个懒人安装包支持CPU运行,会自动识别没有GPU,…...
Windows远程执行
Windows远程执行 前言 1、在办公环境中,利用系统本身的远程服务进行远程代码执行甚至内网穿透横向移动的安全事件是非常可怕的,因此系统本身的一些远程服务在没有必要的情况下建议关闭,防止意外发生; 2、作为安全人员࿰…...
AJAX —— 学习(一)
目录 一、原生 AJAX (一)AJAX 介绍 1.理解 2.作用 3.最大的优势 4.应用例子 (二)XML 介绍 1.理解 2.作用 (三)AJAX 的特点 1.优点 2.缺点 二、HTTP 协议 (一)HTTP 介…...
Activity——idea(2020以后)配置actiBPM
文章目录 前言jar下载idea 安装本地扩展插件 前言 2020及之后版本的idea中,未维护对应的actiBPM扩展插件。如果需要安装该插件,则需要使用本地导入 jar的方式。 jar下载 访问官方网站,搜索对应的actiBPM扩展插件。 https://plugins.jetbra…...
MyBatis——配置优化和分页插件
MyBatis配置优化 MyBatis配置文件的元素结构如下: configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) plugins(插件)…...
[蓝桥杯 2013 省 B] 翻硬币
[蓝桥杯 2013 省 B] 翻硬币 题目背景 小明正在玩一个“翻硬币”的游戏。 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零),比如可能情形是 **oo***oooo,如果…...
[BT]BUUCTF刷题第13天(4.1)
第13天 Upload-Labs-Linux (Basic) Pass-01 根据题目提示,该题为绕过js验证。 一句话木马: <?php eval(system($_POST["cmd"]));?> // 符号 表示后面的语句即使执行错误,也不报错。 // eval() 把括号内的字符串全部…...
特别详细的Spring Cloud 系列教程1:服务注册中心Eureka的启动
Eureka已经被Spring Cloud继承在其子项目spring-cloud-netflix中,搭建Eureka Server的方式还是非常简单的。只需要通过一个独立的maven工程即可搭建Eureka Server。 我们引入spring cloud的依赖和eureka的依赖。 <dependencyManagement><!-- spring clo…...
Day108:代码审计-PHP模型开发篇MVC层动态调试未授权脆弱鉴权未引用错误逻辑
目录 案例1-Xhcms-动态调试-脆弱的鉴权逻辑 案例2-Cwcms-动态调试-未引用鉴权逻辑 案例3-Bosscms-动态调试-不严谨的鉴权逻辑 知识点: 1、PHP审计-动态调试-未授权安全 2、PHP审计-文件对比-未授权安全 3、PHP审计-未授权访问-三种形态 动态调试优点: 环境配置&…...
重读Java设计模式: 桥接模式详解
引言 在软件开发中,经常会遇到需要在抽象与实现之间建立连接的情况。当系统需要支持多个维度的变化时,使用传统的继承方式往往会导致类爆炸和耦合度增加的问题。为了解决这一问题,我们可以使用桥接模式。桥接模式是一种结构型设计模式&#…...
新规解读 | 被网信办豁免数据出境申报义务的企业,还需要做什么?
为了促进数据依法有序自由流动,激发数据要素价值,扩大高水平对外开放,《促进和规范数据跨境流动规定》(以下简称《规定》)对数据出境安全评估、个人信息出境标准合同、个人信息保护认证等数据出境制度作出优化调整。 …...
fakebook-攻防世界
题目 先目录扫描一下 dirseach 打开flag.php是空白的 访问robots.txt,访问user.php.bak <?php class UserInfo { public $name ""; public $age 0; public $blog ""; public function __construct($name, $age, $blog) { …...
大数据领域Doris在农业科技领域的作物生长数据分析
大数据领域Doris在农业科技领域的作物生长数据分析 关键词:Doris数据库、农业大数据、作物生长分析、实时数据处理、多维数据分析、精准农业、时间序列数据 摘要:本文深入探讨Apache Doris在农业科技领域的作物生长数据分析中的应用。通过解析Doris的核心…...
用 JSON 列存储扩展字段后,如何优雅地支持高频查询?MySQL 虚拟列 + 联合索引实战指南
文章目录1. 引言:当业务需要“无限”扩展字段2. 方案回顾:JSON 列存储的优点与痛点2.1 为什么选 JSON 列?2.2 痛点:JSON 内部字段无法直接使用索引3. 虚拟列:把 JSON 字段“抽”出来变成真实列3.1 创建虚拟列提取 JSON…...
交换分区的添加
📌 给 Ubuntu 22.04 服务器添加 Swap 交换分区(解决 2G 内存 OOM 问题) Swap 就是把一部分硬盘空间“借”来当内存用,能有效缓解内存不足导致的进程被杀死(OOM)问题。 1. 先检查当前 Swap 状态 先确认服务…...
千寻百念精选头像小程序源码
内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 千寻百念小程序是一款精美的微信小程序,提供海量高清头像资源,涵盖多种风格和分类。它采用瀑布流布局,支持多端适配,拥有流畅的用户体验&a…...
基本复现-计及碳排放成本的电_气_热综合能源系统节点能价计算方法研究 真正做到了电热气潮流耦合
基本复现-计及碳排放成本的电_气_热综合能源系统节点能价计算方法研究 真正做到了电热气潮流耦合,很适合综合能源系统建模的初学者,配合复现论文。 运行程序HeatGasPowerCombination即可。 每个系统模型都有专门的文档讲解,程序注释齐全。 通…...
解锁论文写作新境界:书匠策AI,你的期刊论文智能导航员
在学术的浩瀚海洋中,每一位研究者都像是勇敢的航海家,而期刊论文则是他们探索未知、分享发现的重要航标。然而,撰写一篇高质量的期刊论文并非易事,它要求研究者不仅具备深厚的专业知识,还需掌握一定的写作技巧与规范。…...
Unity CG着色器实战
卡通风格先一个Pass只渲染背面,黑色,沿法线膨胀,做轮廓线效果;正式渲染Pass,漫反射采样一个逐渐变暗的纹理,做出硬边明暗。高光反射和一个阈值比较,大于则直接显示高光颜色。Shader "My/To…...
QT编程(11):Qt 文本高亮实现代码编辑器
一、功能概述与核心原理 本次基于Qt Widgets实现一款简易代码编辑器,核心实现自定义语法文本高亮、基础代码编辑、行号显示、关键字/注释/字符串区分高亮四大核心功能,适配C/C基础语法高亮规则,可轻松拓展到Python、Java等其他语言。 核心技术…...
你们在OpenClaw上的token消耗如何?
我第一次看 OpenClaw 账单,是凌晨两点。那天刚把它接进飞书群,想着让它帮我盯服务器日志,顺便回答点同事的技术问题。第二天一早打开控制台,token 曲线像心电图一样往上窜。我当时第一反应不是“贵”,而是“它到底在干…...
零基础部署Qwen2.5-7B-Instruct:5分钟搭建本地智能对话助手
零基础部署Qwen2.5-7B-Instruct:5分钟搭建本地智能对话助手 想体验专业级大模型的强大能力,但又担心云端服务的隐私问题和高昂成本?今天,我们就来手把手教你,如何在5分钟内,零基础搭建一个完全运行在你本地…...

