数据结构与算法教程,数据结构C语言版教程!(第五部分、数组和广义表详解)七

news2024/12/26 10:38:06

 第五部分、数组和广义表详解

数组和广义表,都用于存储逻辑关系为“一对一”的数据。

数组存储结构,99% 的编程语言都包含的存储结构,用于存储不可再分的单一数据;而广义表不同,它还可以存储子广义表。

本章重点从矩阵的角度讨论二维数组的存储,同时讲解广义表的存储结构以及有关其广度和深度的算法实现。

十三、广义表的深度和长度(C语言)详解

前面学习了广义表及其对应的存储结构,本节来学习如何计算广义表的深度和长度,以及如何编写相应的 C 语言实现程序。

1、广义表的长度

广义表的长度,指的是广义表中所包含的数据元素的个数。

由于广义表中可以同时存储原子和子表两种类型的数据,因此在计算广义表的长度时规定,广义表中存储的每个原子算作一个数据,同样每个子表也只算作是一个数据。

例如,在广义表 {a,{b,c,d}} 中,它包含一个原子和一个子表,因此该广义表的长度为 2。

再比如,广义表 {{a,b,c}} 中只有一个子表 {a,b,c},因此它的长度为 1。

前面我们用 LS={a1,a2,...,an} 来表示一个广义表,其中每个 ai 都可用来表示一个原子或子表,其实它还可以表示广义表 LS 的长度为 n。

广义表规定,空表 {} 的长度为 0。

在编程实现求广义表长度时,由于广义表的存储使用的是链表结构,且有以下两种方式(如图 1 所示):

图 1 存储 {a,{b,c,d}} 的两种方式

对于图 1a) 来说,只需计算最顶层(红色标注)含有的节点数量,即可求的广义表的长度。

同理,对于图 1b) 来说,由于其最顶层(蓝色标注)表示的此广义表,而第二层(红色标注)表示的才是该广义表中包含的数据元素,因此可以通过计算第二层中包含的节点数量,即可求得广义表的长度。

由于两种算法的实现非常简单,这里只给出计算图 1a) 中广义表长度的 C 语言实现代码:

#include<stdio.h>

#include<stdlib.h>

typedef struct GLNode {

        int tag;//标志域

        union {

                char atom;//原子结点的值域

                struct {

                        struct GLNode* hp, * tp;

                }ptr;//子表结点的指针域,hp指向表头;tp指向表尾

        }subNode;

}*Glist;

Glist creatGlist(Glist C) {

        //广义表C

        C = (Glist)malloc(sizeof(Glist));

        C->tag = 1;

        //表头原子‘a’

        C->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.hp->tag = 0;

        C->subNode.ptr.hp->subNode.atom = 'a';

        //表尾子表(b,c,d),是一个整体

        C->subNode.ptr.tp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->tag = 1;

        C->subNode.ptr.tp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->subNode.ptr.tp = NULL; /

        /开始存放下一个数据元素(b,c,d),表头为‘b’,表尾为(c,d)

        C->subNode.ptr.tp->subNode.ptr.hp->tag = 1;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.hp->tag = 0;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.hp->subNode.atom = 'b';

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp = (Glist)malloc(sizeof(Glist));

        //存放子表(c,d),表头为c,表尾为d

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->tag = 1;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.hp->tag = 0;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.hp->subNode.atom = 'c';

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp = (Glist)malloc(sizeof(Glist));

        //存放表尾d

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->tag = 1;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.hp->tag = 0;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.hp->subNode.atom = 'd';

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.tp = NULL;

        return C;

}

int GlistLength(Glist C) {

        int Number = 0; Glist P = C;

        while (P) {

                Number++;

                P = P->subNode.ptr.tp;

        }

        return Number;

}

int main() {

        Glist C = NULL;

         C = creatGlist(C);

        printf("广义表的长度为:%d", GlistLength(C));

}

程序运行结果为:

广义表的长度为:2

2、广义表的深度

广义表的深度,可以通过观察该表中所包含括号的层数间接得到。

图 2 广义表示意图

从图 2 中可以看到,此广义表从左往右数有两层左括号(从右往左数也是如此),因此该广义表的深度为 2。

再比如,广义表 {{1,2},{3,{4,5}}} 中,子表 {1,2} 和 {3,{4,5}} 位于同层,此广义表中包含 3 层括号,因此深度为 3。

以上观察括号的方法需将广义表当做字符串看待,并借助栈存储结构实现,这里不做重点介绍。

编写程序计算广义表的深度时,以图 1a) 中的广义表为例,可以采用递归的方式:

  • 依次遍历广义表 C 的每个节点,若当前节点为原子(tag 值为 0),则返回 0;若为空表,则返回 1;反之,则继续遍历该子表中的数据元素。
  • 设置一个初始值为 0 的整形变量 max,每次递归过程返回时,令 max 与返回值进行比较,并取较大值。这样,当整个广义表递归结束时,max+1 就是广义表的深度。

其实,每次递归返回的值都是当前所在的子表的深度,原子默认深度为 0,空表默认深度为 1。

C 语言实现代码如下:

#include <stdio.h>

#include <stdlib.h>

typedef struct GLNode {

        int tag;//标志域

        union {

                char atom;//原子结点的值域

                struct {

                        struct GLNode* hp, * tp;

                }ptr;//子表结点的指针域,hp指向表头;tp指向表尾

        }subNode;

}*Glist, GNode;

Glist creatGlist(Glist C) {

        //广义表C

        C = (Glist)malloc(sizeof(Glist));

        C->tag = 1;

        //表头原子‘a’

        C->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.hp->tag = 0;

        C->subNode.ptr.hp->subNode.atom = 'a';

        //表尾子表(b,c,d),是一个整体

        C->subNode.ptr.tp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->tag = 1;

        C->subNode.ptr.tp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->subNode.ptr.tp = NULL;

        //开始存放下一个数据元素(b,c,d),表头为‘b’,表尾为(c,d)

        C->subNode.ptr.tp->subNode.ptr.hp->tag = 1;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.hp->tag = 0;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.hp->subNode.atom = 'b';

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp = (Glist)malloc(sizeof(Glist));

        //存放子表(c,d),表头为c,表尾为d

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->tag = 1;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.hp->tag = 0;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.hp->subNode.atom = 'c';

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp =         (Glist)malloc(sizeof(Glist));

        //存放表尾d

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->tag = 1;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.hp = (Glist)malloc(sizeof(Glist));

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.hp->tag = 0;

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.hp->subNode.atom = 'd';

        C->subNode.ptr.tp->subNode.ptr.hp->subNode.ptr.tp->subNode.ptr.tp->subNode.ptr.tp = NULL;

         return C;

}

int GlistDepth(Glist C) {

        //如果表C为空表时,直接返回长度1;

        if (!C) {

                return 1;

        }

        //如果表C为原子时,直接返回0;

        if (C->tag == 0) {

                return 0;

        }

        int max = 0;//设置表C的初始长度为0;

        for (Glist pp = C; pp; pp = pp->subNode.ptr.tp) {

                int dep = GlistDepth(pp->subNode.ptr.hp);

                if (dep > max) {

                        max = dep;//每次找到表中遍历到深度最大的表,并用max记录

                }

        }

        //程序运行至此处,表明广义表不是空表,由于原子返回的是0,而实际长度是1,所以,此处要+1;

        return max + 1;

}

int main(int argc, const char* argv[]) {

        Glist C = NULL;

        C = creatGlist(C);

        printf("广义表的深度为:%d", GlistDepth(C));

        return 0;

}

程序运行结果:

广义表的深度为:2


十四、广义表的复制详解(含C语言代码实现)

对于任意一个非空广义表来说,都是由两部分组成:表头和表尾。反之,只要确定的一个广义表的表头和表尾,那么这个广义表就可以唯一确定下来。

复制一个广义表,也是不断的复制表头和表尾的过程。如果表头或者表尾同样是一个广义表,依旧复制其表头和表尾。

所以,复制广义表的过程,其实就是不断的递归,复制广义表中表头和表尾的过程。

递归的出口有两个:

  1. 如果当前遍历的数据元素为空表,则直接返回空表。
  2. 如果当前遍历的数据元素为该表的一个原子,那么直接复制,返回即可。

还拿广义表 C 为例:

广义表C的结构示意图

图1 广义表 C 的结构示意图

代码实现:

#include <stdio.h>

#include <stdlib.h>

typedef struct GLNode{

        int tag;//标志域

        union{

                char atom;//原子结点的值域

                struct{

                        struct GLNode * hp,*tp;

                }ptr;//子表结点的指针域,hp指向表头;tp指向表尾

        };

}*Glist,GNode;

Glist creatGlist(Glist C){

        //广义表C

        C=(Glist)malloc(sizeof(GNode));

        C->tag=1;

        //表头原子‘a’

        C->ptr.hp=(Glist)malloc(sizeof(GNode));

        C->ptr.hp->tag=0;

        C->ptr.hp->atom='a';

        //表尾子表(b,c,d),是一个整体

        C->ptr.tp=(Glist)malloc(sizeof(GNode));

        C->ptr.tp->tag=1;

        C->ptr.tp->ptr.hp=(Glist)malloc(sizeof(GNode));

        C->ptr.tp->ptr.tp=NULL;

        //开始存放下一个数据元素(b,c,d),表头为‘b’,表尾为(c,d)

        C->ptr.tp->ptr.hp->tag=1;

        C->ptr.tp->ptr.hp->ptr.hp=(Glist)malloc(sizeof(GNode));

        C->ptr.tp->ptr.hp->ptr.hp->tag=0;

        C->ptr.tp->ptr.hp->ptr.hp->atom='b';

        C->ptr.tp->ptr.hp->ptr.tp=(Glist)malloc(sizeof(GNode));

        //存放子表(c,d),表头为c,表尾为d

        C->ptr.tp->ptr.hp->ptr.tp->tag=1;

        C->ptr.tp->ptr.hp->ptr.tp->ptr.hp=(Glist)malloc(sizeof(GNode));

        C->ptr.tp->ptr.hp->ptr.tp->ptr.hp->tag=0;

        C->ptr.tp->ptr.hp->ptr.tp->ptr.hp->atom='c';

        C->ptr.tp->ptr.hp->ptr.tp->ptr.tp=(Glist)malloc(sizeof(GNode));

        //存放表尾d

        C->ptr.tp->ptr.hp->ptr.tp->ptr.tp->tag=1;

        C->ptr.tp->ptr.hp->ptr.tp->ptr.tp->ptr.hp=(Glist)malloc(sizeof(GNode));

        C->ptr.tp->ptr.hp->ptr.tp->ptr.tp->ptr.hp->tag=0;

        C->ptr.tp->ptr.hp->ptr.tp->ptr.tp->ptr.hp->atom='d';

        C->ptr.tp->ptr.hp->ptr.tp->ptr.tp->ptr.tp=NULL;

        return C;

}

void copyGlist(Glist C, Glist *T){

        //如果C为空表,那么复制表直接为空表

        if (!C) {

                *T=NULL;

        }

        else

        {

                *T=(Glist)malloc(sizeof(GNode));//C不是空表,给T申请内存空间

                //申请失败,程序停止

                if (!*T) {

                        exit(0);

                }

                (*T)->tag=C->tag;//复制表C的tag值

                //判断当前表元素是否为原子,如果是,直接复制

                if (C->tag==0) {

                        (*T)->atom=C->atom;

                }else{

                        //运行到这,说明复制的是子表

                        copyGlist(C->ptr.hp, &((*T)->ptr.hp));//复制表头

                        copyGlist(C->ptr.tp, &((*T)->ptr.tp));//复制表尾

                }

        }

}

int main(int argc, const char * argv[]) {

        Glist C=NULL;

        C=creatGlist(C);

        Glist T=NULL; copyGlist(C,&T);

        printf("%c",T->ptr.hp->atom);

        return 0;

}

运行结果:

a

1、总结

在实现复制广义表的过程中,实现函数为:

void copyGlist(Glist C, Glist *T);

其中,Glist *T,等同于: struct GLNode* *T,此为二级指针,不是一级指针。在主函数中,调用此函数时,传入的是指针 T 的地址,而不是 T 。

这里使用的是地址传递,而不是值传递。如果在这里使用值传递,会导致广义表 T 丢失结点,复制失败。

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

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

相关文章

java复习篇 数据结构:链表第二节 哨兵

目录 单向链表哨兵 初始 头插 思路 代码 尾插 思路 遍历 遍历验证头插 尾插代码 尾插测试 get 思路 代码 测试 insert 思路 代码 测试 remove 移除头结点 提问 移除指定位置 测试 单向链表哨兵 单向链表里面有一个特殊的节点称为哨兵节点&#xff0c;…

[pytorch入门] 2. tensorboard

tensorboard简介 TensorBoard 是一组用于数据可视化的工具。它包含在流行的开源机器学习库 Tensorflow 中.但是也可以独立安装&#xff0c;服务Pytorch等其他的框架 可以常常用来观察训练过程中每一阶段如何输出的 安装pip install tensorboard启动tensorboard --logdir<d…

redis-发布缓存

一.redis的发布订阅 什么 是发布和订阅 Redis 发布订阅 (pub/sub) 是一种消息通信模式&#xff1a;发送者 (pub) 发送消息&#xff0c;订阅者 (sub) 接收消息。 Redis 客户端可以订阅任意数量的频道。 Redis的发布和订阅 客户端订阅频道发布的消息 频道发布消息 订阅者就可…

MATLAB知识点:mode :计算众数

​讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章 3.4.1节 mode &#xff1a;计算众数 众数是指一…

量子网络是什么

量子网络是基于量子力学规律对量子信息进行存储、处理和传输的物理装置&#xff0c;是实现量子通讯和大规模量子计算的基础。清华大学研究团队利用同种离子的双类型量子比特编码&#xff0c;在国际上首次实现无串扰的量子网络节点&#xff0c;对未来实现量子通讯和大规模量子计…

使用Linux SDK客户端向AWS Iot发送数据

参考链接&#xff1a; https://ap-southeast-1.console.aws.amazon.com/iot/home?regionap-southeast-1#/test 此篇文章用于测试&#xff0c;使用Linux SDK客户端向AWS Iot发送数据&#xff0c;准备环境如下&#xff1a; 1、客户端环境准备 1.1 客户端操作系统 虚拟机一台…

Docker中安装 RabbitMQ

1、下载 RabbitMQ 镜像 下载最新版本的镜像&#xff1a; docker pull rabbitmq更多版本的镜像可以访问 Docker 官网&#xff1a;https://hub.docker.com/_/rabbitmq?tabtags 2、创建并运行 RabbitMQ 容器 启动命令&#xff1a; docker run -d -p 15672:15672 -p 5672:567…

音视频基础——音视频录制及播放

音视频录制 Darren老师 当涉及音视频录制时&#xff0c;通常需要从源&#xff08;例如麦克风或摄像头&#xff09;采集音视频数据&#xff0c;并对其进行处理和编码&#xff0c;最后进行封装&#xff0c;以生成最终的音视频文件或流。以下是一般的音视频录制原理的详细步骤&am…

PHP中一些特征函数导致的漏洞总结

第一部分&#xff1a; 特征函数 接触到几个常用的函数&#xff1a; \\ \\\ md5 intval strpos in_array preg_match str_replacephp用这些函数实现过滤一些代码&#xff0c;漏洞可能有一些特性&#xff0c;利用这些特征代码进行对比&#xff1b;账号密码对比&#xff1b;强制检…

leetcode刷题(剑指offer) 105.从前序与中序遍历序列构造二叉树

105.从前序与中序遍历序列构造二叉树 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,…

【江科大】STM32:USART串口(理论部分)上

串口 全双工&#xff1a;可以进行同步通信 单端信号&#xff1a;信号线传输的就是单端信号。&#xff08;也就是与地线&#xff08;GND&#xff09;的电势差&#xff09; 缺点&#xff1a;防干扰能力差 原因&#xff1a;当信号从A点传输到B点&#xff0c;理想条件是A&#xff0…

java steam 的使用

说steam 前看下kotlin的一个写法如果用java怎么写 fun main() {// 创建一个列表val fruits listOf("Apple", "Banana", "Cherry", "Date", "Elderberry")// 使用 Sequence 进行过滤和映射操作val uppercaseFruitLengths …

qt-C++笔记之命令行编译程序,特别是使用Q_OBJECT宏包含了moc(Meta-Object Compiler)的情况

qt-C笔记之命令行编译程序&#xff0c;特别是使用Q_OBJECT宏包含了moc(Meta-Object Compiler)的情况 —— 杭州 2024-01-24 code review! 文章目录 qt-C笔记之命令行编译程序&#xff0c;特别是使用Q_OBJECT宏包含了moc(Meta-Object Compiler)的情况1.问题现象&#xff1a;q…

【华为 ICT HCIA eNSP 习题汇总】——题目集6

1、IEEE 802.11g 标准支持的最大协商速率为&#xff08;&#xff09;。 A、300Mbps B、150Mbps C、54Mbps D、1200Mbps 考点&#xff1a;无线局域网 解析&#xff1a;&#xff08;C&#xff09; IEEE 802.11系列标准如下表&#xff1a; 标准数据传输速率主要技术IEEE 802.111M…

qml与C++的交互

qml端使用C对象类型、qml端调用C函数/c端调用qml端函数、qml端发信号-连接C端槽函数、C端发信号-连接qml端函数等。 代码资源下载&#xff1a; https://download.csdn.net/download/TianYanRen111/88779433 若无法下载&#xff0c;直接拷贝以下代码测试即可。 main.cpp #incl…

Qt/QML编程之路:ListView实现横排图片列表的示例(40)

ListView列表,在QML中使用非常多,排列一个行,一个列或者一个表格,都会用到ListView。 ListView显示从内置QML类型(如ListModel和XmlListModel)创建的模型中的数据,或在C++中定义的从QAbstractItemModel或QAbstract ListModel继承的自定义模型类中的数据。 ListView有一…

计算机服务器中了mallox勒索病毒解密方案计划,勒索病毒解密措施

计算机技术的不断应用与发展&#xff0c;为企业的生产运营提供了有利条件&#xff0c;但网络安全威胁无处不在。近期&#xff0c;广西某生物制药企业的计算机服务器遭到了mallox勒索病毒攻击&#xff0c;导致企业的计算机所有重要数据被加密&#xff0c;严重影响企业的生产运营…

H5嵌入小程序适配方案

时间过去了两个多月&#xff0c;2024已经到来&#xff0c;又老了一岁。头发也掉了好多。在这两个月时间里都忙着写页面&#xff0c;感觉时间过去得很快。没有以前那么轻松了。也不是遇到了什么难点技术&#xff0c;而是接手了一个很烂得项目。能有多烂&#xff0c;一个页面发起…

gin中使用swagger生成接口文档

想要使用gin-swagger为你的代码自动生成接口文档&#xff0c;一般需要下面三个步骤&#xff1a; 按照swagger要求给接口代码添加声明式注释&#xff0c;具体参照声明式注释格式。使用swag工具扫描代码自动生成API接口文档数据使用gin-swagger渲染在线接口文档页面 第一步&…

IDEA远程服务器开发

IDEA的远程开发是在本地去操远程服务器上的代码&#xff0c;可以直接将本地代码的编译,构建,调试,运行等工作都放在远程服务器上而本地运行一个客户端远程去操作服务器上的代码,就如同我们平常写代码一样。相比于云桌面成本更低,开发效率更高。 1.首先服务器配置jdk&#xff0…