9. 广义表 - 广义表概念,存储结构,深度/长度,复制算法

9. 广义表 - 广义表概念,存储结构,深度/长度,复制算法

9.1 广义表的基础概念

1 )什么是广义表

  • 广义表,又称列表,也是一种线性存储结构,既可以存储不可再分的元素,也可以存储广义表,记作:LS = (a1,a2,…,an),其中,LS 代表广义表的名称,an 表示广义表存储的数据,广义表中每个 ai 既可以代表单个元素,也可以代表另一个广义表。

2 )广义表的原子和子表

  • 广义表中存储的单个元素称为 "原子",而存储的广义表称为 "子表"
    例如 :广义表 LS = {1,{1,2,3}},则此广义表的构成 :广义表 LS 存储了一个原子 1 和子表 {1,2,3}。
  • 广义表存储数据的一些常用形式:
    • A = ():A 表示一个广义表,只不过表是空的。
    • B = (e):广义表 B 中只有一个原子 e。
    • C = (a,(b,c,d)) :广义表 C 中有两个元素,原子 a 和子表 (b,c,d)。
    • D = (A,B,C):广义表 D 中存有 3 个子表,分别是A、B和C。这种表示方式等同于 D = ((),(e),(b,c,d)) 。
    • E = (a,E):广义表 E 中有两个元素,原子 a 和它本身。这是一个递归广义表,等同于:E = (a,(a,(a,…)))。

3 ) 广义表的表头和表尾

  • 当广义表不是空表时,称第一个数据(原子或子表)为"表头"剩下的数据构成的新广义表为"表尾"
  • 除非广义表为空表,否则广义表一定具有表头和表尾,且广义表的表尾一定是一个广义表。

9.2 广义表的存储结构

1)存储结构一

  • 存储结构一如下示意图所示:表示原子的节点由两部分构成,分别是 tag 标记位和原子的值,表示子表的节点由三部分构成,分别是 tag 标记位、hp 指针和 tp 指针
    • tag 标记位用于区分此节点是原子还是子表,通常原子的 tag 值为 0,子表的 tag 值为 1;
    • 子表节点中的 hp 指针用于连接本子表中存储的原子或子表;
    • tp 指针用于连接广义表中下一个原子或子表。
      在这里插入图片描述
  • 广义表中两种节点的表示代码定义如下:
    定义中使用了 union 共用体,因为同一时间此节点不是原子节点就是子表节点,当表示原子节点时,就使用 atom 变量;反之则使用 ptr 结构体。
typedef struct GNode{
    int tag;         // 标志域, 0表示原子, 1表示子表
    union{
        char atom;   //  原子结点的值域
        struct{
            struct GNode * hp, *tp;
        }ptr;   // 子表结点的指针域, hp指向表头, tp指向表尾
    }subNode;
}GLNode, *Glist;
  • 例如,广义表 {a,{b,c,d}} 用该存储结构的存储示意图如下 :
    在这里插入图片描述
    2)存储结构二
  • 另一种存储结构的原子的节点也由三部分构成,分别是 : tag 标记位、原子值和 tp 指针构成;表示子表的节点由三部分构成,分别是 : tag 标记位、hp 指针和 tp 指针,示意图如下:
    在这里插入图片描述
  • 代码定义如下:
typedef struct GNode {
    int tag;                // 标志域, 0表示原子, 1表示子表
    union {
        int atom;          // 原子结点的值域
        struct GNode* hp;  // 子表结点的指针域, hp指向表头
    }subNode;
    struct GNode* tp;     // 这里的tp相当于链表的next指针, 用于指向下一个数据元素
}GLNode, *Glist;
  • 例如,广义表 {a,{b,c,d}} 用该存储结构的存储示意图如下 :
    在这里插入图片描述

9.3 广义表的深度和长度

9.3.1 广义表的长度

  • 广义表的长度,指的是广义表中所包含的数据元素的个数
  • 计算元素个数时,广义表中存储的每个原子算作一个数据,同样每个子表也只算作是一个数据
    • LS = {a1,a2,…,an} 的长度为 n;
    • 广义表 {a,{b,c,d}} 的长度为 2;
    • 广义表 {{a,b,c}} 的长度为 1;
    • 空表 {} 的长度为 0。
  • 求广义表长度时,两种不同的存储方式求解也有所不同,如下示意图所示:
    在这里插入图片描述
    对于图 1a) 来说,只需计算最顶层(红色标注)含有的节点数量,即可求的广义表的长度。同理,对于图 1b) 来说,由于其最顶层(蓝色标注)表示的此广义表,而第二层(红色标注)表示的才是该广义表中包含的数据元素,因此可以通过计算第二层中包含的节点数量,才可求得广义表的长度。

9.3.2 广义表的深度

  • 广义表的深度,可以通过观察该表中所包含括号的层数间接得到,如下示例,该广义表的深度为2。
    在这里插入图片描述

9.4 广义表的复制

  • 广义表的复制思想 : 任意一个非空广义表来说,都是由两部分组成:表头和表尾。反之,只要确定的一个广义表的表头和表尾,那么这个广义表就可以唯一确定下来。因此复制一个广义表,也是不断的复制表头和表尾的过程。如果表头或者表尾同样是一个广义表,依旧复制其表头和表尾。
  • 复制广义表的过程,其实就是不断的递归复制广义表中表头和表尾的过程,递归的出口有两个:
    • 如果当前遍历的数据元素为空表,则直接返回空表。
    • 如果当前遍历的数据元素为该表的一个原子,那么直接复制,返回即可
  • 实现代码:
// 广义表的复制, C为复制目标广义表,*T为指向复制后的广义表
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));  //复制表尾
        }
    }
}

感谢阅读 若有错误 敬请见谅!!!


  • 189
    点赞
  • 1036
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
1. 字符串可以被看作是广义表的一种特殊形式,其中每个字符都可以看作是一个原子元素,而字符串本身则是一个只包含原子元素的列表。 2. 广义表复制可以通过递归地复制每个子表和原子元素来实现。具体来说,对于一个广义表L,如果它是一个原子元素,则直接返回该元素;否则,递归地复制它的表头和表尾,并将它们组成一个新的广义表返回。 3. 广义表长度可以通过递归地计算每个子表和原子元素的个数来实现。具体来说,对于一个广义表L,如果它是一个原子元素,则返回1;否则,递归地计算它的表头和表尾的长度,并将它们相加再加1(表示当前表头的长度)。 4. 广义表深度可以通过递归地计算每个子表的深度来实现。具体来说,对于一个广义表L,如果它是一个原子元素,则返回0;否则,递归地计算它的表头和表尾的深度,并将它们中的最大值加1(表示当前表的深度)。 5. 广义表的表头可以通过直接访问链表的头节点来实现。 6. 广义表的表尾可以通过直接访问链表的第二个节点来实现。 7. 由于题目中提到了稀疏矩阵的十字链表存储结构,因此需要先了解这种数据结构的定义和实现方法,然后根据题目要求编写程序来读入三元组并建立稀疏矩阵的十字链表存储结构。接着,可以编写算法来实现矩阵转置,具体来说,可以遍历稀疏矩阵的十字链表,将每个非零元素的行列坐标交换,并将它们插入到转置后的稀疏矩阵的十字链表中。最后,可以输出转置后的稀疏矩阵的十字链表中的三元组。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

园长QwQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值