数据结构之广义表

数据结构之广义表


广义表(Generalized List)是一种重要的线性数据结构,它是线性表的推广。广义表不仅能存储单个元素,还能存储其他广义表,因而具有递归性质。

广义表的定义

  1. 广义表的基本元素

    • 广义表是由一个有限的元素序列组成。
    • 元素可以是原子(即不可分的数据项,如整数、字符等)或子表(即另一个广义表)。
  2. 形式化定义

    • 空表:用 () 表示。
    • 非空表:可以表示为 A = ( a 1 , a 2 , … , a n ) A = (a_1, a_2, \dots, a_n) A=(a1,a2,,an),其中 a i a_i ai 可以是原子或广义表。
    • 递归定义:
      • 空表是广义表。
      • a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an 是广义表或原子,则 ( a 1 , a 2 , … , a n ) (a_1, a_2, \dots, a_n) (a1,a2,,an) 是广义表。
  3. 特点

    • 广义表的深度是其包含的嵌套层次。
    • 一个广义表的长度是其顶层元素的数量(不包括嵌套层次的元素)。

广义表的结构

存储表示

广义表的存储方式一般有以下两种:

  1. 顺序存储
    • 使用数组或字符串存储,适合元素数量已知、结构固定的情况。
    • 存储缺点:嵌套表需要额外的处理逻辑。
  2. 链式存储
    • 广义表的链式存储采用多重链表结构。
    • 每个结点包含两个域:
      • 元素域:存储原子或子表的指针。
      • 指针域:指向下一个元素。

操作与性质

  1. 长度与深度

    • 长度:广义表的长度是表中顶层元素的个数。
    • 深度:广义表的深度是子表嵌套的最大层数
  2. 基本操作

    • 取表头:获取广义表的第一个元素。
    • 取表尾:获取广义表去掉第一个元素后的部分。
    • 插入元素:向广义表指定位置插入原子或子表。
    • 删除元素:从广义表中删除指定位置的元素。

广义表的示例

示例1:简单广义表

A = ( a , b , c ) A = (a, b, c) A=(a,b,c)

  • A A A 是广义表,长度为 3,深度为 1。

示例2:嵌套广义表

B = ( a , ( b , c ) , d ) B = (a, (b, c), d) B=(a,(b,c),d)

  • B B B 的长度为 3(顶层的 3 个元素)。
  • B B B 的深度为 2(因为有一层嵌套)。

示例3:空表

C = ( ) C = () C=()

  • C C C 的长度为 0,深度为 1。

应用场景

  1. 表示层次结构: 如树形结构、图形结构、嵌套列表等。它能够清晰地表示数据之间的包含关系和组织结构。
  2. 解析和处理复杂数据: 如XML、JSON等方面,广义表可以发挥重要作用。它能够帮助程序理解并处理这些具有嵌套结构的数据。
  3. 实现数据结构和算法: 如树、图、堆栈、队列等。可以实现这些复杂的数据结构和算法,从而简化编程过程。
  4. 应用于编程语言和数据库系统: 如LISP、Scheme、MongoDB、ExistDB。这使得开发人员能够更轻松地处理和管理各种数据类型。
  5. 处理自定义数据类型: 广通过广义表,可以轻松地操作和处理自定义数据类型。

优点与不足

优点

  • 能够表示复杂的层次关系。
  • 动态存储、扩展性强。

不足

  • 链式存储的实现复杂,访问效率低于线性表。
  • 操作广义表需要递归处理,效率较低。

广义表的数据结构设计

节点结构定义

广义表的节点需要能够存储两种类型的数据:

  1. 原子(基本数据类型)。
  2. 子表(另一个广义表)。

可以使用 C++ 的 structclass 定义节点:

#include <iostream>
#include <vector>
#include <memory> // 用于智能指针

// 定义广义表节点类型
struct GListNode {
    bool isAtom; // true 表示原子,false 表示子表
    int atom;    // 如果是原子,则存储数据
    std::vector<std::shared_ptr<GListNode>> sublist; // 如果是子表,则存储子节点列表

    // 构造函数
    GListNode(int value) : isAtom(true), atom(value) {}
    GListNode() : isAtom(false) {}
};
  • 字段说明
    • isAtom 用于区分当前节点是原子还是子表。
    • atom 用于存储原子值。
    • sublist 是一个动态数组,存储子表的指针。

广义表的基本操作

创建广义表

使用递归方法创建广义表:

std::shared_ptr<GListNode> createGList() {
    auto node = std::make_shared<GListNode>(); // 创建空表
    node->sublist.push_back(std::make_shared<GListNode>(1)); // 插入原子 1
    node->sublist.push_back(std::make_shared<GListNode>());  // 插入一个子表
    node->sublist[1]->sublist.push_back(std::make_shared<GListNode>(2)); // 子表中插入原子 2
    node->sublist.push_back(std::make_shared<GListNode>(3)); // 插入原子 3
    return node;
}

上述代码生成的广义表可以表示为:
( 1 , ( 2 ) , 3 ) (1, (2), 3) (1,(2),3)

打印广义表

递归遍历广义表并打印:

void printGList(const std::shared_ptr<GListNode>& node) {
    if (node->isAtom) {
        std::cout << node->atom;
    } else {
        std::cout << "(";
        for (size_t i = 0; i < node->sublist.size(); ++i) {
            printGList(node->sublist[i]);
            if (i != node->sublist.size() - 1) std::cout << ", ";
        }
        std::cout << ")";
    }
}

计算广义表的长度和深度

  1. 计算长度(顶层元素个数):
size_t getLength(const std::shared_ptr<GListNode>& node) {
    if (!node->isAtom) {
        return node->sublist.size();
    }
    return 0; // 原子没有子节点
}
  1. 计算深度(嵌套层次):
size_t getDepth(const std::shared_ptr<GListNode>& node) {
    if (node->isAtom) {
        return 0; // 原子没有嵌套
    }
    size_t maxDepth = 0;
    for (const auto& child : node->sublist) {
        maxDepth = std::max(maxDepth, getDepth(child));
    }
    return maxDepth + 1;
}

插入元素

  1. 元素可以插入到广义表的指定位置。
  2. 插入的元素可以是原子或子表。
  3. 插入位置是相对于当前子表的索引。
void insertElement(std::shared_ptr<GListNode>& node, size_t position, const std::shared_ptr<GListNode>& newElement) {
    if (node->isAtom) {
        std::cerr << "不能在原子节点插入元素!" << std::endl;
        return;
    }
    if (position > node->sublist.size()) {
        std::cerr << "插入位置超出范围!" << std::endl;
        return;
    }
    node->sublist.insert(node->sublist.begin() + position, newElement);
}

实现插入到嵌套子表

  • 遍历广义表,找到目标子表(例如 (2))。
  • 对该子表执行插入操作。
void insertIntoSublist(std::shared_ptr<GListNode>& node, size_t sublistIndex, size_t position, const std::shared_ptr<GListNode>& newElement) {
    if (node->isAtom) {
        std::cerr << "目标不是子表,无法插入!" << std::endl;
        return;
    }

    // 确保索引合法
    if (sublistIndex >= node->sublist.size()) {
        std::cerr << "子表索引超出范围!" << std::endl;
        return;
    }

    // 定位目标子表
    auto& targetSublist = node->sublist[sublistIndex];

    if (targetSublist->isAtom) {
        std::cerr << "目标索引处是原子,无法插入!" << std::endl;
        return;
    }

    // 插入到目标子表中
    if (position > targetSublist->sublist.size()) {
        std::cerr << "插入位置超出子表范围!" << std::endl;
        return;
    }
    targetSublist->sublist.insert(targetSublist->sublist.begin() + position, newElement);
}

删除元素

void deleteElement(std::shared_ptr<GListNode>& node, size_t position) {
    if (node->isAtom) {
        std::cerr << "不能从原子节点删除元素!" << std::endl;
        return;
    }
    if (position >= node->sublist.size()) {
        std::cerr << "删除位置超出范围!" << std::endl;
        return;
    }
    node->sublist.erase(node->sublist.begin() + position);
}

删除子表中的元素

  1. 需要首先找到目标子表,确保其是一个子表(isAtom = false)。
  2. 然后在该子表的 sublist 中定位到要删除的元素的索引。
  3. 从子表的 sublist 中删除目标元素。
void deleteFromSublist(std::shared_ptr<GListNode>& node, size_t sublistIndex, size_t position) {
    if (node->isAtom) {
        std::cerr << "目标不是子表,无法删除!" << std::endl;
        return;
    }

    // 确保子表索引合法
    if (sublistIndex >= node->sublist.size()) {
        std::cerr << "子表索引超出范围!" << std::endl;
        return;
    }

    auto& targetSublist = node->sublist[sublistIndex];

    // 确保目标是子表
    if (targetSublist->isAtom) {
        std::cerr << "目标索引处是原子,无法删除!" << std::endl;
        return;
    }

    // 确保删除位置有效
    if (position >= targetSublist->sublist.size()) {
        std::cerr << "删除位置超出子表范围!" << std::endl;
        return;
    }

    // 删除元素
    targetSublist->sublist.erase(targetSublist->sublist.begin() + position);
}

完整示例代码

#include <iostream>
#include <vector>
#include <memory>

struct GListNode
{
    bool isAtom;
    int atom;
    std::vector<std::shared_ptr<GListNode>> sublist;

    GListNode(int value) : isAtom(true), atom(value) {}
    GListNode() : isAtom(false) {}
};

std::shared_ptr<GListNode> createGList()
{
    auto node = std::make_shared<GListNode>();
    node->sublist.push_back(std::make_shared<GListNode>(1));
    node->sublist.push_back(std::make_shared<GListNode>());
    node->sublist[1]->sublist.push_back(std::make_shared<GListNode>(2));
    node->sublist.push_back(std::make_shared<GListNode>(3));
    return node;
}

void printGList(const std::shared_ptr<GListNode>& node)
{
    if (node->isAtom)
    {
        std::cout << node->atom;
    }
    else
    {
        std::cout << "(";
        for (size_t i = 0; i < node->sublist.size(); ++i)
        {
            printGList(node->sublist[i]);
            if (i != node->sublist.size() - 1) std::cout << ", ";
        }
        std::cout << ")";
    }
}

size_t getLength(const std::shared_ptr<GListNode>& node)
{
    if (!node->isAtom)
    {
        return node->sublist.size();
    }
    return 0;
}

size_t getDepth(const std::shared_ptr<GListNode>& node)
{
    if (node->isAtom)
    {
        return 0;
    }
    size_t maxDepth = 0;
    for (const auto& child : node->sublist)
    {
        maxDepth = std::max(maxDepth, getDepth(child));
    }
    return maxDepth + 1;
}

void insertElement(std::shared_ptr<GListNode>& node, size_t position, const std::shared_ptr<GListNode>& newElement)
{
    if (node->isAtom)
    {
        std::cerr << "不能在原子节点插入元素!" << std::endl;
        return;
    }
    if (position > node->sublist.size())
    {
        std::cerr << "插入位置超出范围!" << std::endl;
        return;
    }
    node->sublist.insert(node->sublist.begin() + position, newElement);
}

void insertIntoSublist(std::shared_ptr<GListNode>& node, size_t sublistIndex, size_t position, const std::shared_ptr<GListNode>& newElement)
{
    if (node->isAtom)
    {
        std::cerr << "目标不是子表,无法插入!" << std::endl;
        return;
    }

    if (sublistIndex >= node->sublist.size())
    {
        std::cerr << "子表索引超出范围!" << std::endl;
        return;
    }

    auto& targetSublist = node->sublist[sublistIndex];

    if (targetSublist->isAtom)
    {
        std::cerr << "目标索引处是原子,无法插入!" << std::endl;
        return;
    }

    if (position > targetSublist->sublist.size())
    {
        std::cerr << "插入位置超出子表范围!" << std::endl;
        return;
    }
    targetSublist->sublist.insert(targetSublist->sublist.begin() + position, newElement);
}

void deleteElement(std::shared_ptr<GListNode>& node, size_t position)
{
    if (node->isAtom)
    {
        std::cerr << "不能从原子节点删除元素!" << std::endl;
        return;
    }
    if (position >= node->sublist.size())
    {
        std::cerr << "删除位置超出范围!" << std::endl;
        return;
    }
    node->sublist.erase(node->sublist.begin() + position);
}

void deleteFromSublist(std::shared_ptr<GListNode>& node, size_t sublistIndex, size_t position)
{
    if (node->isAtom)
    {
        std::cerr << "目标不是子表,无法删除!" << std::endl;
        return;
    }

    // 确保子表索引合法
    if (sublistIndex >= node->sublist.size())
    {
        std::cerr << "子表索引超出范围!" << std::endl;
        return;
    }

    auto& targetSublist = node->sublist[sublistIndex];

    // 确保目标是子表
    if (targetSublist->isAtom)
    {
        std::cerr << "目标索引处是原子,无法删除!" << std::endl;
        return;
    }

    // 确保删除位置有效
    if (position >= targetSublist->sublist.size())
    {
        std::cerr << "删除位置超出子表范围!" << std::endl;
        return;
    }

    // 删除元素
    targetSublist->sublist.erase(targetSublist->sublist.begin() + position);
}

int main()
{
    auto gList = createGList();
    std::cout << "初始广义表:";
    printGList(gList);
    std::cout << std::endl;
    std::cout << "长度: " << getLength(gList) << std::endl;
    std::cout << "深度: " << getDepth(gList) << std::endl;

    // 插入操作
    auto newAtom = std::make_shared<GListNode>(4);
    insertElement(gList, 1, newAtom);
    std::cout << "插入一个元素:" << std::endl;
    printGList(gList);
    std::cout << std::endl;
    std::cout << "长度: " << getLength(gList) << std::endl;
    std::cout << "深度: " << getDepth(gList) << std::endl;

    // 插入到子表 (2) 的末尾
    insertIntoSublist(gList, 2, 0, newAtom);
    std::cout << "在子表中插入一个元素:" << std::endl;
    printGList(gList);
    std::cout << std::endl;
    std::cout << "长度: " << getLength(gList) << std::endl;
    std::cout << "深度: " << getDepth(gList) << std::endl;

    // 删除操作
    deleteElement(gList, 1);
    std::cout << "删除后广义表:";
    printGList(gList);
    std::cout << std::endl;
    std::cout << "长度: " << getLength(gList) << std::endl;
    std::cout << "深度: " << getDepth(gList) << std::endl;

    deleteFromSublist(gList, 1, 1);
    std::cout << "删除后广义表中的子表后:";
    printGList(gList);
    std::cout << std::endl;
    std::cout << "长度: " << getLength(gList) << std::endl;
    std::cout << "深度: " << getDepth(gList) << std::endl;

    return 0;
}

运行结果

对于广义表 gList = (1, (2), 3)

初始广义表:(1, (2), 3)
长度: 3
深度: 2
插入一个元素:
(1, 4, (2), 3)
长度: 4
深度: 2
在子表中插入一个元素:
(1, 4, (4, 2), 3)
长度: 4
深度: 2
删除后广义表:(1, (4, 2), 3)
长度: 3
深度: 2
删除后广义表中的子表后:(1, (4), 3)
长度: 3
深度: 2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值