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

链表(详解)

一、链表

1.1、什么是链表

1、链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,有一系列结点(地址)组成,结点可动态的生成。

2、结点包括两个部分:(1)存储数据元素的数据域(内存空间),(2)存储指向下一个结点地址的指针域。

3、相对于线性表顺序结构,操作复杂。

1.2、链表的分类

链表的结构非常多样,以下的情况组合起来就有8种链表结构

(1)单项和双向

(2)带头和不带头

(3)循环和不循环

1.3、链表和顺序表的比较

(1)数组:使用一块连续的内存空间地址去存放数据,但

例如:
int  a[5]={1,2,3,4,5}。突然我想继续加两个数据进去,但是已经定义好的数组不能往后加,只能通过定义新的数组

int b[7]={1,2,3,4,5,6,7};  这样就相当不方便比较浪费内存资源,对数据的增删不好操作。

(2)链表:使用多个不连续的内存空间去存储数据, 可以 节省内存资源(只有需要存储数据时,才去划分新的空间),对数据的增删比较方便

注意:

1.链式结构在逻辑上是连续的,但在物理上不一定连续

2.现实中的结点一般都是从堆上申请出来的

3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续

二、无头单向非循环链表

2.1、无头单向非循环链表的结构

链表有一个数据域存放数据,一个指针域存放下一个结点的地址。

typedef int SLTDataType;typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;

2.2、无头单向非循环链表的实现

//打印
void SLTPrint(SLTNode* phead);//创建一个新节点
SLTNode* BuySListNode(SLTDataType x);//尾增
void SLTPushBack(SLTNode** pphead, SLTDataType x);//头增
void SLTPushFront(SLTNode** pphead, SLTDataType x);//尾删
void SLTPopBack(SLTNode** pphead);//头删
void SLTPopFront(SLTNode** pphead);// 作业
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);// 在pos之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);// 在pos以后插入x   
void SLTInsertAfter(SLTNode* pos, SLTDataType x);// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos);// 删除pos的后一个位置
void SLTPopAfter(SLTNode* pos);// 单链表的销毁
void SListDestroy(SLTNode** pphead);

2.2.1、创建一个新节点

SLTNode* BuySListNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc");exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}

创建一个新节点,用malloc开辟一个链表节点空间,强制转换成链表结构体,将data置为X,将next置为空,并返回新节点。

2.2.2、单链表的尾插

//单链表的尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySListNode(x);//没有一个节点if (*pphead == NULL){*pphead = newnode;}else{SLTNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}

单链表的尾插首先需要判断是否是空链表,如果为空就把该节点置为头节点,若不为空,先便利找到尾结点,然后将新节点插入尾节点后面。

2.2.3、单链表的头插法

//单链表的头插法   效率高,简单
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySListNode(x);newnode->next = *pphead;*pphead = newnode;
}

头插法相对简单,只需要将新节点插到头结点的前面,并且将头结点指针赋给新节点。

2.2.4、单链表的尾删

//单链表的尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead);//空assert(*pphead);// 1个节点if ((*pphead)->next == NULL){free((*pphead));*pphead = NULL;}else  //两个或者多个节点{   //方法一 /*SLTNode* tail = *pphead;while (tail->next->next){tail = tail->next;}free(tail->next);tail->next = NULL;*///方法二SLTNode* tail = *pphead;SLTNode* tailprev = NULL;while (tail->next){tailprev = tail;tail = tail->next;}free(tail);tail = NULL;tailprev->next = NULL;}
}

和尾插法一样,首先先判断链表是否只有一个节点或者没有节点(为空),将会最后一个链表置空,如果超过一个节点,先找到倒数第二个节点,然后置空最后一个节点,将倒数第二个节点的next置空

2.2.5、单链表的头删法

//链表的头删法   效率高,简单
void SLTPopFront(SLTNode** pphead)
{assert(pphead);assert(*pphead);SLTNode* newnode = (*pphead)->next;free(*pphead);*pphead = newnode;
}

free第一个节点,将头指针后移一位。

2.2.6、单链表的查找

//查找元素  修改
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}

借助cur指针,便利链表,cur=cur->next;若cur->data==x,返回cur,没找到返回NULL。

2.2.7、在pos之前插入

//在pos之前插入
//  传头指针是因为有可能时头插
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pos);if (pos == *pphead){SLTNode* newnode = BuySListNode(x);newnode->next = *pphead;*pphead = newnode;}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}SLTNode* newnode = BuySListNode(x);prev->next = newnode;newnode->next = pos;}
}

在pos位置插入,相对

2.2.8、在pos之后插入

//在pos之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next=newnode;}

2.2.9、删除pos位置

//删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pos);if (pos == *pphead){SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);//pos == NULL;  //可有可无,因为pos只是形参,对他的操作不影响外部的节点}
}

2.2.10、删除pos后一位置

//删除pos后一位置
void SLTPopAfter(SLTNode* pos)
{assert(pos);assert(pos->next == NULL);SLTNode* posnext = pos->next;pos->next = posnext->next;free(posnext);posnext = NULL;
}
//删除一个pos,没有头节点
// 把pos下一个节点的值赋给pos,将下一个节点删除
//但是无法删除尾结点

2.2.11、单链表的销毁

//单链表的销毁
void SListDestroy(SLTNode** pphead)
{assert(*pphead);SLTNode* pre = *pphead;SLTNode* p = pre->next;while (p!=NULL){free(pre);pre = p;p = p->next;}free(pre->next);pre->next = NULL;
}

三、带头双向循环链表

双向链表的原理与单链表类似,双向链表需要两个指针来链接,一个指向前面的,一个指向后面的。同时需要一个head,头链表,方便操作。

3.1带头双向链表实现

3.1.1、创建结构体

typedef int DataType;
typedef struct ListNode
{struct ListNode *next;struct ListNode *pre;DataType data;
}LTNode;

此结构中比单链表结构增加一个结构体指针pre,用于存放上一个节点的地址。
next是存放一个节点的地址。
data是存放数据。

3.1.2、申请结点

LTNode* BuyListNode(DataType x)//申请结点
{LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL){perror( "malloc fail");exit(-1);}node->next = NULL;node->pre = NULL;node->data = x;return node;
}

动态申请结点,函数返回的是一个指针类型,用malloc开辟一个LTNode大小的空间,并用node指向这个空间,再判断是否为空,如为空就perror,显示错误信息。反之则把要存的数据x存到newnode指向的空间里面,把指针置为空。

3.1.3、初始化创建头结点

LTNode* LTInit()//初始化创建头结点
{LTNode* phead = BuyListNode(0);phead->next = phead;phead->pre = phead;return phead;
}

单链表开始是没有节点的,可以定义一个指向空指针的结点指针,但是此链表不同,需要在初始化函数中创建个头结点,它不用存储有效数据。因为链表是循环的,在最开始需要让头结点的next和pre指向头结点自己。
因为其他函数也不需要用二级指针(因为头结点指针是不会变的,变的是next和pre,改变的是结构体,只需要用结构体针即可,也就是一级指针)为了保持一致此函数也不用二级指针,把返回类型设置为结构体指针类型。

3.1.4、打印链表

void LTPrint(LTNode* phead)//打印链表
{assert(phead);LTNode* cur = phead->next;while (cur!=phead){printf("%d ", cur->data);cur = cur->next;}printf("\n");
}

打印链表,先断言phead,它不能为空,再把头结点下个地址存到cur中,用while循环去遍历,终止条件是等于头指针停止,因为他是循环的,并更新cur。

3.1.5、在pos位置之前插入

void LTInsert(LTNode* pos, DataType x)//在pos位置之前插入数据
{assert(pos);LTNode* node = BuyListNode(x);LTNode* bef = pos->pre;bef->next = node;node->pre = bef;node->next = pos;pos->pre = node;
}

断言pos,不能为空,插入数据先申请一结点放到定义的node指针变量中,为了不用考虑插入顺序,先把pos前面的存到bef中,然后就可以随意链接:
bef指向新节点,新节点前驱指针指向bef,新节点指向pos,pos前驱指针指向新节点。

3.1.6、删除任意位置数据

void LTErase(LTNode* pos)//删除pos位置数据
{assert(pos);pos->pre->next = pos->next;pos->next->pre = pos->pre;free(pos);
}

删除把pos位置之前的结点直接指向pos的下一个结点,把pos下一个结点的前驱指针指向pos之前的结点。

3.1.7、尾插

void LTPushBack(LTNode* phead, DataType x)//尾插
{/*assert(phead);//复杂方法/*LTNode* newnode = BuyListNode(x);LTNode* tail = phead->prev;tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;*/assert(phead);//简便方法LTInsert(phead, x);
}

简便方法:尾插是在尾部插入,用简便方法调用LTInsert函数,传入头指针和x。

复杂方法是:申请结点newnode,把头指针前的上一个结点存到尾指针变量中,再双向链接newnode,最后还得把头和尾(刚申请的结点)循环起来。

3.1.8、尾删

void LTPopBack(LTNode* phead)//尾删
{//assert(phead);//复杂方法//assert(phead->next != phead);  // 空//LTNode* tail = phead->prev;//LTNode* tailPrev = tail->prev;//tailPrev->next = phead;//phead->prev = tailPrev;//free(tail);assert(phead);//简便方法assert(phead->next != phead);  // 空LTErase(phead->pre);
}

简便方法:因为是尾删,删的是尾部,直接调用LTErase函数传入头指针的上一个结点,也就是尾部,因为是双向循环不用遍历直接直到尾部。

复杂方法:先把头结点上一个结点地址存起来,再把尾部的上一个结点地址存起来,再把第二次存的直接链接头部,头部链接第二次存的结点,再把第一次的结点释放掉。

3.1.9、头插

void LTPushFront(LTNode* phead, DataType x)//头插
{//assert(phead);//复杂方法//LTNode* newnode = BuyListNode(x);//LTNode* back = phead->next;//phead->next = newnode;//newnode->prev = phead;//newnode->next = back;//back->prev = newnode;assert(phead);//简便方法LTInsert(phead->next, x);
}

简便方法:因为是头插直接调用LTInsert函数传 头结点下一个结点指针和x。

复杂方法:申请结点存到newnode,再把头结点下一个结点地址存到指针back里,头部和新节点和back,三节点双向链接。

3.1.10、头删

void LTPopFront(LTNode* phead)//头删
{//assert(phead);//assert(phead->next != phead); // 空/*LTNode* back = phead->next;LTNode* second = back->next;free(back);phead->next = second;second->prev = phead;*/assert(phead);assert(phead->next != phead);  // 空LTErase(phead->next);}

简便方法:因为头删,直接调LTErase函数传入头结点下一个指针。

复杂方法:先把头结点下一个结点地址存到back指针里,再把back一个结点地址存到second指针里,先释放中间的back,最后头结点和second双向链接。

3.1.11、查找元素

LTNode* LTFind(LTNode* phead, DataType x)//查找
{assert(phead);LTNode* cur = phead->next;while (cur!=phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;}

查找把头结点下一个结点存到cur,然后用while循环遍历,终止条件是cur等于头结点指针,如果cur等于x,直接返回cur指针,再更新cur,最后遍历完返回NULL,表示没有该数据。

3.1.12、释放链表

void LTDestroy(LTNode* phead)//释放链表
{assert(phead);LTNode* cur = phead->next;while (cur != phead){LTNode* next = cur->next;free(cur);cur = next;}free(phead);
}

释放链表从头开始释放,把头结点下一个结点存到cur中,再用用while循环,终止条件是cur不等于头指针,在里面把cur下一个指针存到next中,释放掉cur,再把next更新为cur。
最后头结点也是申请的,也得释放。

3.1.13、判断是否为空

bool LTEmpty(LTNode* phead)//判断是否为空
{assert(phead);return phead->next == phead;
}

3.1.14、求链表长度

size_t LTSize(LTNode* phead)//求链表长度
{assert(phead);size_t size = 0;LTNode* cur = phead->next;while (cur != phead){++size;cur = cur->next;}return size;
}

求链表长度,先把头结点下一个结点存到cur中,再用while循环遍历终止条件是cur等于头结点,用size++记录长度,并更新cur,最后返回size,32位机器下是无符号整型size_t。


到这里链表的基本问题就解释完了,相信多多少少会解决大家心头的疑问,在数据结构的学习中应当善于思考,多画图,死磕代码,注意细节,将伪代码转换为代码,这样才能很好的掌握数据结构的有关知识,共勉,加油!!!

相关文章:

链表(详解)

一、链表 1.1、什么是链表 1、链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,有一系列结点(地址)组成,结点可动态的生成。 2、结点包括两个部分:&#x…...

最简单vue获取当前地区天气--高德开放平台实现

目录 前言 一、注册成为高德平台开发者 二、注册天气key 1.点击首页右上角打开控制台 2.创建新应用 三、vue项目使用 1.打开vue项目找到public下的index.html,如果是vue3的话直接在主目录打开index.html文件就行,主要就是打开出口文件 ​编辑 2.根据高德…...

大数据处理 正则表达式去除特殊字符 提取中文英文数字

在文本处理中,经常会碰到含有特殊字符的字符串。 比如用户昵称, 小红书文案,等等 都包含了大量表情特殊字符。 这些特殊字符串在ETL处理过程中,经常会引起程序报错,导致致命错误,程序崩溃;或者导…...

Python装饰器(decorators)

本文改编自以下文章:Decorators in Python 装饰器是一个很强大的工具,它允许我们很便捷地修改已有函数或者类的功能,我们可以用装饰器把另一个函数包装起来,扩展一些功能而不需要去修改这个函数代码。 预备知识 在Python中&…...

[halcon] 局部图片保存 gen_circle 和 gen_rectangle2 对比 这怕不是bug吧

背景 我想实现一个功能,获取图片中瑕疵的位置,将瑕疵周边的一块区域抠图并保存。 上代码 一开始我代码这么写的: gen_circle (Rectangle, Row[i], Column[i], 256) reduce_domain(Image,Rectangle,GrayEllipse) crop_domain(GrayEllipse,…...

解析msvcp100.dll丢失的原因及修复方法,教你快速解决的方案

msvcp100.dll文件的丢失,其实也是属于dll丢失的其中一种,因为它是dll文件,大家记住,只要是后缀是dll的文件那么它就是dll文件,只要丢失了dll文件,那么其解决的方法都是大同小异的,唯一不同的是&…...

算法:模拟思想算法

文章目录 实现原理算法思路典型例题替换所有问号提莫攻击N字型变换外观序列 总结 本篇总结的是模拟算法 实现原理 模拟算法的实现原理很简单,就是依据题意实现题意的目的即可,考察的是你能不能实现题目题意的代码能力 算法思路 没有很明显的算法思路…...

【base64】JavaScriptuniapp 将图片转为base64并展示

Base64是一种用于编码二进制数据的方法&#xff0c;它将二进制数据转换为文本字符串。它的主要目的是在网络传输或存储过程中&#xff0c;通过将二进制数据转换为可打印字符的形式进行传输 JavaScript 压缩图片 <html><body><script src"https://code.j…...

根据一个List生成另外一个List,修改其中一个,导致另外一个List也在变化

1、两个List复制 SysDic aSysDic new SysDic(); aSysDic.setDkey("1"); aSysDic.setDnote("12"); SysDic bSysDic new SysDic(); bSysDic.setDkey("2"); bSysDic.setDnote("23"); …...

Cesium 加载 geojson 文件并对文件中的属性值进行颜色设置

文章目录 需求分析解决 需求 Cesium 加载 geojson 文件并对文件中的属性值进行颜色设置 分析 在搜寻多种解决方案后&#xff0c;最后总结出 自己的解决方案 方案一&#xff0c;没看懂 var geojsonOptions {clampToGround : true //使数据贴地};var entities;promise Cesium…...

windows系统配置tcp最大连接数

打开注册表 运行->regedit HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters目录下 增加一个MaxUserPort&#xff08;默认值是5000&#xff0c;端口范围是1025至5000&#xff09;MaxUserPort设置为65534&#xff08;需重启服务器&#xff09; 执行dos命令&…...

SQL存储过程中 SET ANSI_NULLS ON 和 SET QUOTED_IDENTIFIER ON的作用和详解

今天在写SQL Server存储过程中遇到的&#xff0c;做个整理归纳 USE [ABInbevDB] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO实际上&#xff0c;我们在创建存储过程的时候&#xff0c;这几行的代码是会自动创建出来的&#xff0c;那么先解释下两个标准的概念。 两个…...

C语言——程序执行的三大流程

顺序 : 从上向下&#xff0c; 顺序执行代码分支 : 根据条件判断&#xff0c; 决定执行代码的分支循环 : 让特定代码重复的执行...

二级MySQL(十)——单表查询

这里我们只在一个表内查询&#xff0c;用到的是较为简单的SELECT函数形式 1、查询指定的字段&#xff1a; 用到的数据库是之前提到的S、P、SP数据库 S表格用到的总数据&#xff1a; 首先我们查询所有供应商的序号和名字 这时都是独立的&#xff0c;没有关系&#xff0c;我们找…...

机器学习:无监督学习

文章目录 线性学习方法聚类ClusteringKmeansHAC 分布表示降维PCAMatrix FactorizationManifold LearningLLELaplacian Eigenmapst-SEN 线性学习方法 聚类Clustering Kmeans 随机选取K个中心&#xff0c;然后计算每个点与中心的距离&#xff0c;找最近的&#xff0c;然后更新中…...

计算机网络之5层网络协议

文章目录 引言一、OSI七层模型二、TCP/IP参考模型三、网络协议的概念和作用四、TCP/IP参考模型每层详细介绍1.物理层2.数据链路层1. 基本概念2.MAC地址3.ARP协议 3. 网络层1. 基本概念2.ip协议3.子网掩码 4. 传输层1. 基本概念2. 协议3. TCP&#xff08;三次握手四次挥手&#…...

常见前端面试之VUE面试题汇总十一

31. Vuex 有哪几种属性&#xff1f; 有五种&#xff0c;分别是 State、 Getter、Mutation 、Action、 Module state > 基本数据(数据源存放地) getters > 从基本数据派生出来的数据 mutations > 提交更改数据的方法&#xff0c;同步 actions > 像一个装饰器&a…...

2021年12月 C/C++(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;书架 John最近买了一个书架用来存放奶牛养殖书籍&#xff0c;但书架很快被存满了&#xff0c;只剩最顶层有空余。 John共有N头奶牛(1 ≤ N ≤ 20,000)&#xff0c;每头奶牛有自己的高度Hi(1 ≤ Hi ≤ 10,000)&#xff0c;N头奶牛的总高度为S。书架高度为B(1 ≤…...

解决 git clone 时出现Failed to connect to 127.0.0.1 port 1573问题

今天去拉一个仓库代码&#xff0c;往常都是一下就拉下来了&#xff0c;今天却报错&#xff0c;报错信息如下&#xff1a; 原因&#xff1a;这种情况是因为代理在git中配置的&#xff0c;但是本身环境就有SSL协议了&#xff0c;所以取消git的https或者http代理即可 方法如下&…...

日本核污染水排海,有必要囤盐吗?

据央视新闻24日报道&#xff0c;当地时间8月24日13时&#xff0c;日本福岛第一核电站启动污水排海。消息一出&#xff0c;全球哗然。虽然事情已经过去了几天&#xff0c;但是&#xff0c;随着这一举动&#xff0c;大家就乱了阵脚&#xff0c;恐惧者有之&#xff0c;辱骂者有之&…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...