数据结构C语言描述5(图文结合)--广义表讲解与实现

news2024/11/28 18:00:06

前言

  • 这个专栏将会用纯C实现常用的数据结构和简单的算法;
  • 有C基础即可跟着学习,代码均可运行;
  • 准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言实现的原因之一;
  • 欢迎收藏 + 关注,本人将会持续更新。

文章目录

    • 广义表定义
    • 广义表常用表示
    • 广义表的深度
    • 广义表实现

广义表定义

广义表是线性表的推广,也被称为列表(lists),广义表一般记作:
LS=(a1,a2,a3,…,an
其中,LS是表的名称,n是表的长度。在线性表的定义中,ai (1<=i<=n)只能限于单个元素,但是在广义表中ai可以是 单个元素,也可以是广义表,分别称为广义表LS的原子子表。 当广义表非空时,第一个元素(a1)被称为表头,其余元素(a2,a3,…,a n)被称为子表。

广义表常用表示

  • **E=():**E是一个空表,其长度为0。
    • 广义表()和(())不同
      • 前者是长度为0的空表
      • 后者是长度为l的非空表(只不过该表中惟一的一个元素是空表)
  • **L=(a,b) :**L是长度为2的广义表,它的两个元素都是原子,因此它是一个线性表
  • **A=(x,L)=(x,(a,b)):**A是长度为2的广义表,第一个元素是原子x,第二个元素是子表L。
  • **B=(A,y)=((x,(a,b)),y):**B是长度为2的广义表,第一个元素是子表A,第二个元素是原子y。
  • **C=(A,B)=((x,(a,b)),((x,(a,b)),y)):**C的长度为2,两个元素都是子表。
  • **D=(a,D)=(a,(a,(a,(…)))):**D的长度为2,第一个元素是原子,第二个元素是D自身,展开后它是一个无限的广义表。

2️⃣ 两层广义表

在这里插入图片描述

3️⃣ 三层广义表

在这里插入图片描述

4️⃣ 四层广义表

在这里插入图片描述

广义表的深度

一个表的"深度"是指表展开后所含括号的层数。

  • E=():深度为1
  • L=(a,b) :深度为1
  • A=(x,L)=(x,(a,b)):深度为2
  • B=(A,y)=((x,(a,b)),y):深度为3
  • C=(A,B)=((x,(a,b)),((x,(a,b)),y)):深度为4
  • D=(a,D)=(a,(a,(a,(…)))):深度为∞

广义表实现

  • 创建广义表
  • 遍历广义表
  • 求广义表深度
  • 删除广义表

📦 封装广义表节点

typedef struct GeneralizedList {
	bool flag;     // true:表节点, false:原子节点
	union {
		DataType data;    // 原节点,储存数据
		struct GeneralizedList* glist;    // 表节点,储存表
	};
	struct GeneralizedList* next;
}GList;

📑 创建广义表

  • 空表定义:#
  • 递归思路:
    • 返回值:void,参数:当前节点指针;
    • 递归结束条件:顺序执行完即可;
    • 单层递归逻辑:以(x,(a,b))为例
      • 从左到右,遍历,如果是#,说明是空表,直接赋值为null即可,如果是(,则需要创建表然后递归指向表节点,如果是x,即是原子节点,则创建原子节点,递归指向下一个节点
      • 后面一种情况是,,这两个,如果是,,则说明递归去创建下一个节点,如果是),则说明这一层表已经到头了,next指向NULL
/*
	空表:#
	例子:
	(x,(a,b))
	((x,(a,b)),y,(z,f))
*/
void create_glist(GList** list)
{
	assert(list);

	char key;
	scanf_s("%c", &key, 1);
	// 从做到有开始读,首先有三种情况,#,(, 数据
	if (key == '#') {
		*list = NULL;
	}
	else if (key == '(') {
		*list = (GList*)calloc(1, sizeof(GList));
		assert(*list);
		(*list)->flag = true;
		create_glist(&((*list)->glist));
	}
	else {    // 读取数据
		*list = (GList*)calloc(1, sizeof(GList));
		assert(*list);
		(*list)->flag = false;
		(*list)->data = key;   // 读取数据
	}

	// 其他情况:','  ')'
	scanf_s("%c", &key, 1);

	if (*list == NULL) {  // 空表后面不进行任何操作,GList为NULL
		return;
	}
	else if (key == ',') {
		create_glist(&((*list)->next));
	}
	else if (key == ')' || key == '\n') {   // '\n' 是因为出入数据中有换行,这个时候就是的一个广义表创建完成
		(*list)->next = NULL;
	}
}

🏠 递归遍历

  • 存储结构:以节点形式存储,如下图:

    • 在这里插入图片描述
  • 递归思路:

    • 返回值:void,参数:节点指针
    • 结束条件:从上到下顺序即可
    • 单层递归逻辑:
      • 如果是表节点,打印(,看这个表节点是否存储了节点list->glist == NULL(没有,则打印#),否则,就递归遍历表节点指向的这个表,后打印)
      • 如果不是表节点,则是原子节点,则打印数据即可
      • 打印打印完数据或者后,如果下一个next指向不为NULL,则打印,
// 遍历,脑子有广义表图形
void print_glist(GList* list)
{
	if (list->flag == true) {
		printf("(");
		if (list->glist == NULL)
			printf("#");
		else
			print_glist(list->glist);
		// 递归完成,这个时候就是一个表打印完成
		printf(")");
	}
	else {
		printf("%c", list->data);
	}

	if (list->next != NULL) {
		printf(",");
		print_glist(list->next);
	}
}

📘 求深度

递归思路:

  • 返回值:最大层数,参数:节点
  • 递归结束条件:节点为空,或者这个节点为原子节点
  • 单层递归逻辑:
    • 一层一层,从左到右边
    • 下图是回溯过程:
// 场景:不为空、且是表节点,则递归
// 画图思路:先以一层为例返回
int depth_glist(GList* list)
{
	int max_depth = 0;
	if (list == NULL) {
		return 0;
	}

	if (list->flag == false) {
		return 0;
	}

	GList* t = list;
	while (t) {
		int res = depth_glist(list->glist);
		max_depth = max_depth > res ? max_depth : res;
		t = t->next;
	}
	return max_depth + 1;
}

删除节点

递归逻辑:

  • 返回值:void,参数:节点指针
  • 结束条件:从上到下顺序即可
  • 单层递归逻辑:
    • 如果这个节点数据是要删除的数据,则按照链表删除方式即可,接着递归。
    • 如果不是,则判断:如果是表节点,则递归表,如果是原子节点,则递归下一个节点。
// 要修改指针的指向
// 递归条件:不相等、为表节点
void erase(GList** list, char k)
{
	if (*list == NULL) {
		return;
	}

	if ((*list)->data == k) {
		GList* t = *list;
		*list = (*list)->next;
		free(t);
		t = NULL;
		erase(list, k);
	}
	else {
		if ((*list)->flag == true) {
			erase(&(*list)->glist, k);
		}
		else {
			erase(&(*list)->next, k);
		}
	}
}

总代码

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int DataType;

typedef struct GeneralizedList {
	bool flag;     // true:表节点, false:原子节点
	union {
		DataType data;    // 原节点,储存数据
		struct GeneralizedList* glist;    // 表节点,储存表
	};
	struct GeneralizedList* next;
}GList;


/*
	(x,(a,b))
	空表:#
	((x,(a,b)),y,(z,f))
*/
void create_glist(GList** list)
{
	assert(list);

	char key;
	scanf_s("%c", &key, 1);
	// 从做到有开始读,首先有三种情况,#,(, 数据
	if (key == '#') {
		*list = NULL;
	}
	else if (key == '(') {
		*list = (GList*)calloc(1, sizeof(GList));
		assert(*list);
		(*list)->flag = true;
		create_glist(&((*list)->glist));
	}
	else {    // 读取数据
		*list = (GList*)calloc(1, sizeof(GList));
		assert(*list);
		(*list)->flag = false;
		(*list)->data = key;   // 读取数据
	}

	// 其他情况:','  ')'
	scanf_s("%c", &key, 1);

	if (*list == NULL) {  // 空表后面不进行任何操作,GList为NULL
		return;
	}
	else if (key == ',') {
		create_glist(&((*list)->next));
	}
	else if (key == ')' || key == '\n') {   // '\n' 是因为出入数据中有换行,这个时候就是的一个广义表创建完成
		(*list)->next = NULL;
	}
}

// 遍历,脑子有广义表图形
void print_glist(GList* list)
{
	if (list->flag == true) {
		printf("(");
		if (list->glist == NULL)
			printf("#");
		else
			print_glist(list->glist);
		// 递归完成,这个时候就是一个表打印完成
		printf(")");
	}
	else {
		printf("%c", list->data);
	}

	if (list->next != NULL) {
		printf(",");
		print_glist(list->next);
	}
}

// 场景:不为空、且是表节点,则递归
// 画图思路:先以一层为例返回
int depth_glist(GList* list)
{
	int max_depth = 0;
	if (list == NULL) {
		return 0;
	}

	if (list->flag == false) {
		return 0;
	}

	GList* t = list;
	while (t) {
		int res = depth_glist(list->glist);
		max_depth = max_depth > res ? max_depth : res;
		t = t->next;
	}
	return max_depth + 1;
}

// 要修改指针的指向
// 递归条件:不相等、为表节点
void erase(GList** list, char k)
{
	if (*list == NULL) {
		return;
	}

	if ((*list)->data == k) {
		GList* t = *list;
		*list = (*list)->next;
		free(t);
		t = NULL;
		erase(list, k);
	}
	else {
		if ((*list)->flag == true) {
			erase(&(*list)->glist, k);
		}
		else {
			erase(&(*list)->next, k);
		}
	}
}

int main()
{
	GList* list = NULL;

	create_glist(&list);

	print_glist(list);

	putchar('\n');

	printf("max_depth: %d\n", depth_glist(list));

	erase(&list, 'b');

	print_glist(list);


	return 0;
}

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

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

相关文章

23种设计模式-装饰器(Decorator)设计模式

文章目录 一.什么是装饰器设计模式&#xff1f;二.装饰器模式的特点三.装饰器模式的结构四.装饰器模式的优缺点五.装饰器模式的 C 实现六.装饰器模式的 Java 实现七.代码解析八.总结 类图&#xff1a; 装饰器设计模式类图 一.什么是装饰器设计模式&#xff1f; 装饰器模式&…

构建英语知识网站:Spring Boot框架解析

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

数据结构之数组与链表的差异

一、数组 数组&#xff08;Array&#xff09;是由相同类型的元素&#xff08;element&#xff09;的集合所组成的数据结构&#xff0c;分配一块连续的内存来存储。利用元素的索引&#xff08;index&#xff09;可以计算出该元素对应的存储地址。最简单的数据结构类型是一维数组…

RabbitMQ7:消息转换器

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

Ubuntu20.04+ROS 进行机械臂抓取仿真:环境搭建(一)

目录 一、从官网上下载UR机械臂 二、给UR机械臂添加夹爪 三、报错解决 本文详细介绍如何在Ubuntu20.04ROS环境中为Universal Robots的UR机械臂添加夹爪。首先从官方和第三方源下载必要的软件包&#xff0c;包括UR机械臂驱动、夹爪插件和相关依赖。然后&#xff0c;针对gazeb…

(即插即用模块-Attention部分) 二十、(2021) GAA 门控轴向注意力

文章目录 1、Gated Axial-Attention2、代码实现 paper&#xff1a;Medical Transformer: Gated Axial-Attention for Medical Image Segmentation Code&#xff1a;https://github.com/jeya-maria-jose/Medical-Transformer 1、Gated Axial-Attention 论文首先分析了 ViTs 在训…

Git 进程占用报错-解决方案

背景 大仓库&#xff0c;由于开发者分支较多&#xff0c;我们在使用 git pull 或 git push 等命令时&#xff08;与远端仓库交互的命令&#xff09;&#xff0c;不知之前配置了什么&#xff0c;我的电脑会必现以下报错&#xff08;有非常长一大串报错-不同分支的git进程占用报…

【FPGA-MicroBlaze】串口收发以及相关函数讲解

前言 工具&#xff1a;Vivado2018.3及其所对应的SDK版本 目前网上有许多MicroBlaze 的入门教程&#xff0c;比如下面的这个参考文章&#xff0c;用串口打印一个hello world。 【FPGA】Xilinx MicroBlaze软核使用第一节&#xff1a;Hello World!_fpga软核microblaze-CSDN博客 个…

代码美学2:MATLAB制作渐变色

效果&#xff1a; %代码美学&#xff1a;MATLAB制作渐变色 % 创建一个10x10的矩阵来表示热力图的数据 data reshape(1:100, [10, 10]);% 创建热力图 figure; imagesc(data);% 设置颜色映射为“cool” colormap(cool);% 在热力图上添加边框 axis on; grid on;% 设置热力图的颜色…

Android下载出现open failed: EPERM (Operation not permitted)

今天帮忙给同事调一下apk&#xff0c;发现android 自动更新apk&#xff0c;下载apk的时候总是失败&#xff0c;总是卡在 输出流这一步了 于是第一步分析&#xff0c;立马想到权限 但是下载之前的读写内存的权限也都有了 什么android 10高版本的不开启分区存储也用了 android…

使用爬虫时,如何确保数据的准确性?

在数字化时代&#xff0c;数据的准确性对于决策和分析至关重要。本文将探讨如何在使用Python爬虫时确保数据的准确性&#xff0c;并提供代码示例。 1. 数据清洗 数据清洗是确保数据准确性的首要步骤。在爬取数据后&#xff0c;需要对数据进行清洗&#xff0c;去除重复、无效和…

uniapp中使用Mescroll实现下拉刷新与上拉加载项目实战

如何在UniApp中使用Mescroll实现下拉刷新与上拉加载 前言 下拉刷新和上拉加载更多成为了提升用户体验不可或缺的功能。UniApp作为一个跨平台的应用开发框架&#xff0c;支持使用Vue.js语法编写多端&#xff08;iOS、Android、H5等&#xff09;应用。Mescroll作为一款专为Vue设…

【接口自动化测试】一文从0到1详解接口测试协议!

接口自动化测试是软件开发过程中重要的环节之一。通过对接口进行测试&#xff0c;可以验证接口的功能和性能&#xff0c;确保系统正常运行。本文将从零开始详细介绍接口测试的协议和规范。 定义接口测试协议 接口测试协议是指用于描述接口测试的规范和约定。它包含了接口的请求…

RAG数据拆分之PDF

引言RAG数据简介PDF解析方法及工具代码实现总结 二、正文内容 引言 本文将介绍如何将RAG数据拆分至PDF格式&#xff0c;并探讨PDF解析的方法和工具&#xff0c;最后提供代码示例。 RAG数据简介 RAG&#xff08;关系型属性图&#xff09;是一种用于表示实体及其关系的图数据…

【开源项目】2024最新PHP在线客服系统源码/带预知消息/带搭建教程

简介 随着人工智能技术的飞速发展&#xff0c;AI驱动的在线客服系统已经成为企业提升客户服务质量和效率的重要工具。本文将探讨AI在线客服系统的理论基础&#xff0c;并展示如何使用PHP语言实现一个简单的AI客服系统。源码仓库地址&#xff1a;ym.fzapp.top 在线客服系统的…

WEB攻防-通用漏洞XSS跨站MXSSUXSSFlashXSSPDFXSS

演示案例&#xff1a; UXSS-Edge&CVE-2021-34506 FlashXSS-PHPWind&SWF反编译 PDFXSS-PDF动作添加&文件上传 使用jpexs反编译swf文件 上传后&#xff0c;发给别人带漏洞的分享链接

QSqlTableModel的使用

实例功能 这边使用一个实例显示数据库 demodb 中 employee 数据表的内容&#xff0c;实现编辑、插入、删除的操作&#xff0c;实现数据的排序和记录过滤&#xff0c;还实现 BLOB 类型字段 Photo 中存储照片的显示、导入等操作&#xff0c;运行界面如下图&#xff1a; 在上图中…

适用于学校、医院等低压用电场所的智能安全配电装置

引言 电力&#xff0c;作为一种清洁且高效的能源&#xff0c;极大地促进了现代生活的便捷与舒适。然而&#xff0c;与此同时&#xff0c;因使用不当或维护缺失等问题&#xff0c;漏电、触电事件以及电气火灾频发&#xff0c;对人们的生命安全和财产安全构成了严重威胁&#xf…

LabVIEW实现UDP通信

目录 1、UDP通信原理 2、硬件环境部署 3、云端环境部署 4、UDP通信函数 5、程序架构 6、前面板设计 7、程序框图设计 8、测试验证 本专栏以LabVIEW为开发平台&#xff0c;讲解物联网通信组网原理与开发方法&#xff0c;覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合…

java——Spring MVC的工作流程

Spring MVC的工作流程是基于模型-视图-控制器&#xff08;MVC&#xff09;设计模式的一个典型实现&#xff0c;以下是其主要工作流程步骤&#xff1a; 客户端请求提交&#xff1a; 用户通过浏览器向服务器发送请求&#xff0c;该请求首先到达Spring MVC的前端控制器DispatcherS…