数据结构之广义表
数据结构之广义表
广义表(Generalized List)是一种重要的线性数据结构,它是线性表的推广。广义表不仅能存储单个元素,还能存储其他广义表,因而具有递归性质。
广义表的定义
-
广义表的基本元素:
- 广义表是由一个有限的元素序列组成。
- 元素可以是原子(即不可分的数据项,如整数、字符等)或子表(即另一个广义表)。
-
形式化定义:
- 空表:用
()
表示。 - 非空表:可以表示为 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) 是广义表。
- 空表:用
-
特点:
- 广义表的深度是其包含的嵌套层次。
- 一个广义表的长度是其顶层元素的数量(不包括嵌套层次的元素)。
广义表的结构
存储表示
广义表的存储方式一般有以下两种:
- 顺序存储:
- 使用数组或字符串存储,适合元素数量已知、结构固定的情况。
- 存储缺点:嵌套表需要额外的处理逻辑。
- 链式存储:
- 广义表的链式存储采用多重链表结构。
- 每个结点包含两个域:
- 元素域:存储原子或子表的指针。
- 指针域:指向下一个元素。
操作与性质
-
长度与深度:
- 长度:广义表的长度是表中顶层元素的个数。
- 深度:广义表的深度是子表嵌套的最大层数。
-
基本操作:
- 取表头:获取广义表的第一个元素。
- 取表尾:获取广义表去掉第一个元素后的部分。
- 插入元素:向广义表指定位置插入原子或子表。
- 删除元素:从广义表中删除指定位置的元素。
广义表的示例
示例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。
应用场景
- 表示层次结构: 如树形结构、图形结构、嵌套列表等。它能够清晰地表示数据之间的包含关系和组织结构。
- 解析和处理复杂数据: 如XML、JSON等方面,广义表可以发挥重要作用。它能够帮助程序理解并处理这些具有嵌套结构的数据。
- 实现数据结构和算法: 如树、图、堆栈、队列等。可以实现这些复杂的数据结构和算法,从而简化编程过程。
- 应用于编程语言和数据库系统: 如LISP、Scheme、MongoDB、ExistDB。这使得开发人员能够更轻松地处理和管理各种数据类型。
- 处理自定义数据类型: 广通过广义表,可以轻松地操作和处理自定义数据类型。
优点与不足
优点
- 能够表示复杂的层次关系。
- 动态存储、扩展性强。
不足
- 链式存储的实现复杂,访问效率低于线性表。
- 操作广义表需要递归处理,效率较低。
广义表的数据结构设计
节点结构定义
广义表的节点需要能够存储两种类型的数据:
- 原子(基本数据类型)。
- 子表(另一个广义表)。
可以使用 C++ 的 struct
或 class
定义节点:
#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 << ")";
}
}
计算广义表的长度和深度
- 计算长度(顶层元素个数):
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);
}
实现插入到嵌套子表
- 遍历广义表,找到目标子表(例如
(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);
}
删除子表中的元素
- 需要首先找到目标子表,确保其是一个子表(
isAtom = false
)。 - 然后在该子表的
sublist
中定位到要删除的元素的索引。 - 从子表的
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