数据结构中的宝藏秘籍之广义表

广义表,也被称作列表(Lists),是一种递归的数据结构。它就像一个神秘的盒子,既可以装着单个元素(原子),也可以嵌套着其他的盒子(子列表)。比如广义表 (a (b c) d),其中 a 和 d 是原子,而 (b c) 则是一个子列表。这种嵌套结构使得广义表能够轻松表示各种复杂的关系,就像一位万能的画家,能描绘出树形结构、图结构等多样的 “画作”。

与普通的线性表相比,广义表的元素类型更加丰富多样,不仅可以是相同类型的元素,还能包含不同类型的元素,甚至可以嵌套其他的广义表。这种灵活性让广义表在处理复杂数据时游刃有余,成为了数据结构中的 “多面手”。

先上代码:

#include <stdio.h>
#include <stdlib.h>// 定义原子类型为字符型
typedef char AtomType;// 定义元素标签类型,用于区分原子和列表
typedef enum { ATOM, LIST } ElemTag;// 定义广义表节点结构体
typedef struct GLNode 
{ElemTag tag;  // 标记该节点是原子还是列表union {AtomType atom;  // 如果是原子,存储原子的值struct GLNode *child;  // 如果是列表,指向子列表} UNION;struct GLNode *next;  // 指向下一个节点
} GLNODE;// 定义元素标志类型,用于读取字符串时识别不同元素
typedef enum { LP = 1, RP = 2, Atom = 3, End = 4 } ElemFlag;// 从 str[*pi] 开始读入一个元素
ElemFlag GetElem(char str[], int *pi, AtomType *pe)
{while (str[*pi] == ' ') (*pi)++;// 跳过空格if (str[*pi] == '\0') return End;// 如果到达字符串末尾,返回 End 标志if (str[*pi] == '(')// 如果遇到左括号,返回 LP 标志,并将指针后移{		(*pi)++;return LP;}if (str[*pi] == ')') // 如果遇到右括号,返回 RP 标志,并将指针后移{(*pi)++;return RP;}// 如果是原子,将字符赋值给 pe,并将指针后移,返回 Atom 标志*pe = str[*pi];(*pi)++;return Atom;
}// 建广义表
GLNODE *Glist_Create(char str[], int *pi) 
{GLNODE *p;AtomType e;// 根据读取的元素类型进行不同处理switch (GetElem(str, pi, &e)) {case Atom:// 为原子节点分配内存p = (GLNODE *)malloc(sizeof(GLNODE));if (p == NULL) {fprintf(stderr, "内存分配失败\n");exit(EXIT_FAILURE);}p->tag = ATOM;p->UNION.atom = e;p->next = Glist_Create(str, pi);// 递归创建下一个节点return p;case LP:// 为列表节点分配内存p = (GLNODE *)malloc(sizeof(GLNODE));if (p == NULL) {fprintf(stderr, "内存分配失败\n");exit(EXIT_FAILURE);}p->tag = LIST;p->UNION.child = Glist_Create(str, pi);// 递归创建子列表p->next = Glist_Create(str, pi);// 递归创建下一个节点return p;case RP:return NULL;case End:return NULL;}return NULL;
}// 求表深度
int GList_Depth(GLNODE *L) 
{GLNODE *p;int depth1, max = 0;if (L == NULL || L->tag == ATOM) return 0;// 如果是原子节点,深度为 0for (p = L->UNION.child; p; p = p->next) // 遍历子列表{depth1 = GList_Depth(p);// 递归计算子列表的深度if (depth1 > max) max = depth1;}return max + 1;// 列表深度为子列表最大深度加 1
}// 遍历广义表,打印层次括号
void GList_Traverse(GLNODE *L) 
{GLNODE *p;for (p = L; p != NULL; p = p->next) {if (p->tag == ATOM) {printf("%c ", p->UNION.atom);// 打印原子节点的值} else {printf("(");// 遇到列表节点,打印左括号GList_Traverse(p->UNION.child);// 递归遍历子列表printf(")");// 打印右括号}}
}// 复制广义表
GLNODE *GList_Copy(GLNODE *L) 
{GLNODE *head = NULL, *p, *newNode, *tail = NULL;if (!L) return NULL;for (p = L; p != NULL; p = p->next) {// 为新节点分配内存newNode = (GLNODE *)malloc(sizeof(GLNODE));if (newNode == NULL) {fprintf(stderr, "内存分配失败\n");exit(EXIT_FAILURE);}if (head == NULL) {head = tail = newNode;} else {tail->next = newNode;tail = newNode;}if (p->tag == ATOM) {newNode->tag = ATOM;// 复制原子节点newNode->UNION.atom = p->UNION.atom;} else {newNode->tag = LIST;// 复制列表节点,递归复制子列表newNode->UNION.child = GList_Copy(p->UNION.child);}}if (tail) {tail->next = NULL;}return head;
}int main() 
{char str[30] = "(a (b c) d)";  // 广义表的字符串表示int i = 0;GLNODE *L1, *L2;L1 = Glist_Create(str, &i);		// 创建广义表 L1GList_Traverse(L1);				// 遍历并打印广义表 L1printf("%d\n", GList_Depth(L1));// 计算并打印广义表 L1 的深度L2 = GList_Copy(L1);			// 复制广义表 L1 到 L2GList_Traverse(L2);				// 遍历并打印广义表 L2printf("%d\n", GList_Depth(L2));// 计算并打印广义表 L2 的深度return 0;
}

(一)数据结构定义:搭建广义表的基石

typedef struct GLNode 
{ElemTag tag;  // 标记该节点是原子还是列表union {AtomType atom;  // 如果是原子,存储原子的值struct GLNode *child;  // 如果是列表,指向子列表} UNION;struct GLNode *next;  // 指向下一个节点
} GLNODE;

这段代码定义了广义表的节点结构 GLNODEtag 字段就像一个神奇的标签,能够区分节点是原子还是列表。UNION 联合体则根据 tag 的值,巧妙地存储原子或指向子列表的指针。next 指针则负责将各个节点连接起来,形成一个有序的链条。这种设计让广义表能够灵活地表示不同类型的元素和复杂的嵌套结构,为后续的操作奠定了坚实的基础。

(二)广义表的创建:从字符串到数据结构的神奇转变

GLNODE *Glist_Create(char str[], int *pi) 
{// ...
}

Glist_Create 函数是广义表创建的核心。它通过 GetElem 函数从输入的字符串中逐个读取元素,就像一位细心的工匠,根据元素的类型(原子、左括号、右括号、字符串结束)进行不同的处理。遇到原子时,为其创建一个原子节点;遇到左括号时,创建一个列表节点,并递归地创建子列表。递归的方式让代码简洁而高效,能够轻松应对广义表的嵌套结构,就像一位技艺高超的魔术师,将字符串神奇地转化为广义表的数据结构。

(三)广义表的深度计算:探索广义表的 “深度” 秘密

int GList_Depth(GLNODE *L) 
{// ...
}

GList_Depth 函数用于计算广义表的深度。对于原子节点,其深度为 0;对于列表节点,则递归地计算子列表的深度,并取子列表最大深度加 1 作为当前列表的深度。递归的思想在这里再次发挥了重要作用,让我们能够轻松地探索广义表的 “深度” 秘密,就像一位勇敢的探险家,深入广义表的内部,测量其嵌套的层次。

(四)广义表的遍历:揭开广义表的 “庐山真面目”

void GList_Traverse(GLNODE *L) 
{// ...
}

GList_Traverse 函数就像一位导游,带领我们遍历广义表并打印其层次括号表示。遇到原子节点时,直接打印原子的值;遇到列表节点时,先打印左括号,递归地遍历子列表,再打印右括号。通过递归遍历,我们能够清晰地看到广义表的层次结构,揭开它的 “庐山真面目”,仿佛置身于一个神秘的迷宫中,一步步揭开它的神秘面纱。

(五)广义表的复制:克隆一个一模一样的广义表

GLNODE *GList_Copy(GLNODE *L) 
{// ...
}

GList_Copy 函数用于复制广义表。它遍历原广义表的每个节点,为新节点分配内存,并根据节点类型复制原子或递归地复制子列表。这样,我们就可以得到一个与原广义表结构完全相同的新广义表,就像克隆技术一样,复制出一个一模一样的 “双胞胎”。

运行:

四、广义表的应用场景:无处不在的 “魔法工具”

(一)编译器设计:解析代码的得力助手

在编译器中,广义表可以用于表示语法树。语法树是源代码的一种抽象表示,它反映了代码的语法结构。广义表的嵌套结构正好可以用来表示语法树的层次关系,方便编译器进行语法分析和代码生成。就像一位聪明的翻译官,将源代码翻译成计算机能够理解的机器语言。

(二)人工智能:构建知识图谱的强大武器

在人工智能领域,广义表可以用于表示知识图谱。知识图谱是一种语义网络,用于表示实体之间的关系。广义表可以将实体和关系表示为节点和子列表,从而方便进行知识的存储和推理。就像一位智慧的导师,帮助人工智能系统更好地理解和处理知识。

(三)图形处理:绘制复杂图形的神奇画笔

在图形处理中,广义表可以用于表示复杂的图形结构。例如,一个三维模型可以由多个子模型组成,每个子模型又可以由多个基本图形组成。广义表可以很好地表示这种层次结构,方便进行图形的渲染和处理。就像一位天才的画家,用神奇的画笔绘制出绚丽多彩的图形世界。

五、总结与展望

通过递归的方式,我们可以方便地实现广义表的创建、深度计算、遍历和复制等操作。

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

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

相关文章

【jenkins】首次配置jenkins

第一步&#xff0c;输入管理员密码 cat /var/jenkins_home/secrets/initialAdminPassword第二步&#xff0c;点击安装推荐的插件 第三步&#xff0c;创建管理员用户 第四步&#xff0c;返回实例 第五步&#xff0c; 升级jenkins 第六步&#xff0c; 修复提示 第七步&#xff0c…

Android studio—socketIO库return与emit的使用

文章目录 一、Socket.IO库简单使用说明1. 后端 Flask Flask-SocketIO2. Android 客户端集成 Socket.IO3. 布局文件注意事项 二、接受服务器消息的二种方法1. 客户端接收通过 emit 发送的消息功能使用场景后端代码&#xff08;Flask-SocketIO&#xff09;客户端代码&#xff08…

用Prompt 技术【提示词】打造自己的大语言智能体

机器如何按照人类的指令执行任务的探索 机器需具备理解任务叙述的能力&#xff0c;以便能够按照人类的指令执行任务&#xff0c;为机器提供一些范例作为参考&#xff0c;使其能够理解该执行的任务类型。这样的学习方式称为“Instruction learning”&#xff0c;透过精心设计的…

Node.js 数据库 事务 项目示例

1、参考&#xff1a;JavaScript语言的事务管理_js 函数 事务性-CSDN博客 或者百度搜索&#xff1a;Nodejs控制事务&#xff0c; 2、实践 2.1、对于MySQL或MariaDB&#xff0c;你可以使用mysql或mysql2库&#xff0c;并结合Promise或async/await语法来控制事务。 使用 mysql2…

【Mamba】MambaVision论文阅读

文章目录 MambaVision一、研究背景&#xff08;一&#xff09;Transformer vs Mamba​&#xff08;二&#xff09;Mamba in CV​ 二、相关工作​&#xff08;一&#xff09;Transformer 在计算机视觉领域的进展​&#xff08;二&#xff09;Mamba 在计算机视觉领域的探索​ 三、…

前端面试宝典---原型链

引言----感谢大佬的讲解 大佬链接 原型链示意图 原型链问题中需要记住一句话&#xff1a;一切变量和函数都可以并且只能通过__proto__去找它所在原型链上的属性与方法 原型链需要注意的点 看上图可以发现 函数&#xff08;构造函数&#xff09;也可以通过__proto__去找到原…

C语言---FILE结构体

一、FILE 结构体的本质与定义 基本概念 FILE 是 C 语言标准库中用于封装文件操作的结构体类型&#xff0c;定义于 <stdio.h> 中。它代表一个“文件流”&#xff0c;可以是磁盘文件、标准输入输出&#xff08;stdin/stdout/stderr&#xff09;或其他输入输出设备。 实现特…

基于大模型的直肠息肉诊疗全流程风险预测与方案优化研究报告

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 二、大模型技术概述 2.1 大模型原理简介 2.2 大模型在医疗领域应用现状 三、直肠息肉术前预测与准备 3.1 基于大模型的术前风险预测 3.1.1 息肉性质预测 3.1.2 手术难度预测 3.2 基于预测结果的术前准备 3.…

华为OD机试真题——MELON的难题(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析&#xff1b; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式&#xff01; 2025华为OD真题目录全流程解析/备考攻略/经验分享 华为OD机试真题《MELON的…

AI数据分析与BI可视化结合:解锁企业决策新境界

大家好&#xff0c;今天我们来聊聊一个前沿而热门的话题——AI数据分析与BI可视化结合&#xff0c;如何携手推动企业决策迈向新高度。在数据爆炸的时代&#xff0c;企业如何高效利用这些数据&#xff0c;成为制胜的关键。AI数据分析与BI可视化的结合&#xff0c;正是解锁这一潜…

克服储能领域的数据处理瓶颈及AI拓展

对于储能研究人员来说&#xff0c;日常工作中经常围绕着一项核心但有时令人沮丧的任务&#xff1a;处理实验数据。从电池循环仪的嗡嗡声到包含电压和电流读数的大量电子表格&#xff0c;研究人员的大量时间都花在了提取有意义的见解上。长期以来&#xff0c;该领域一直受到对专…

【SpringBoot+Vue自学笔记】002 SpringBoot快速上手

跟着这位老师学习的&#xff1a;https://www.bilibili.com/video/BV1nV4y1s7ZN?vd_sourceaf46ae3e8740f44ad87ced5536fc1a45 最好和老师的idea版本完全一致&#xff01;截至本文写的当日最新的idea好像默认jdk17&#xff0c;配置时遇到很多bug。 &#x1f33f; Spring Boot&a…

SpringAI+DeepSeek大模型应用开发——2 大模型应用开发架构

目录 2.大模型开发 2.1 模型部署 2.1.1 云服务-开放大模型API 2.1.2 本地部署 搜索模型 运行大模型 2.2 调用大模型 接口说明 提示词角色 ​编辑 会话记忆问题 2.3 大模型应用开发架构 2.3.1 技术架构 纯Prompt模式 FunctionCalling RAG检索增强 Fine-tuning …

蓝桥杯12. 日期问题

日期问题 原题目链接 题目描述 小明正在整理一批历史文献。这些历史文献中出现了很多日期。 小明知道这些日期都在 1960 年 1 月 1 日 至 2059 年 12 月 31 日 之间。 令小明头疼的是&#xff0c;这些日期采用的格式非常不统一&#xff1a; 有的采用 年/月/日有的采用 月…

STM32使用rand()生成随机数并显示波形

一、随机数生成 1、加入头文件&#xff1a;#include "stdlib.h" 2、定义一个用作生成随机数种子的变量并加入到滴答定时器中不断自增&#xff1a;uint32_t run_times 0; 3、设置种子&#xff1a;srand(run_times);//每次生成随机数前调用一次为佳 4、生成一个随…

『前端样式分享』联系我们卡片式布局 自适应屏幕 hover动效 在wikijs中使用 (代码拿来即用)

目录 预览效果分析要点响应式网格布局卡片样式&#xff1a;阴影和过渡效果 代码优化希望 长短不一的邮箱地址在左右居中的同时,做到左侧文字对齐(wikijs可用)总结 欢迎关注 『前端布局样式』 专栏&#xff0c;持续更新中 欢迎关注 『前端布局样式』 专栏&#xff0c;持续更新中…

【ubuntu】在Linux Yocto的基础上去适配Ubuntu的wifi模块

一、修改wifi的节点名 1.找到wifi模块的PID和VID ifconfig查看wifi模块网络节点的名字&#xff0c;发现是wlx44876393bb3a&#xff08;wlxmac地址&#xff09; 通过udevadm info -a /sys/class/net/wlx44876393bba路径的命令去查看wlx44876393bba的总线号&#xff0c;端口号…

健康养生:开启活力生活新篇章

在当代社会&#xff0c;熬夜加班、久坐不动、外卖快餐成为许多人的生活常态&#xff0c;随之而来的是各种亚健康问题。想要摆脱身体的疲惫与不适&#xff0c;健康养生迫在眉睫&#xff0c;它是重获活力、拥抱美好生活的关键。​ 应对不良饮食习惯带来的健康隐患&#xff0c;饮…

【verilog】多个 if 控制同一个变量(后面会覆盖前面)非阻塞赋值真的并行吗?

非阻塞赋值 (<) 是“并行”的&#xff0c;但是代码顺序会影响结果&#xff1f;”这正是 Verilog 的硬件描述本质 vs 行为语义之间的微妙之处。 &#x1f4a1;1. 非阻塞赋值真的并行吗&#xff1f; 是的&#xff01;非阻塞赋值 < 从行为上是并行的&#xff0c;也就是说&a…

前沿篇|CAN XL 与 TSN 深度解读

引言 1. CAN XL 标准演进与设计目标 2. CAN XL 物理层与帧格式详解 3. 时间敏感网络 (TSN) 关键技术解析 4. CAN XL + TSN 在自动驾驶领域的典型应用