数据结构与C++ 知识量:9 - 32 - 91
广义表(Lists,又称列表)是一种非线性的数据结构,是线性表的一种推广。即广义表中放松对表元素的原子限制,容许它们具有其自身结构。它被广泛的应用于人工智能等领域的表处理语言LISP语言中。在LISP语言中,广义表是一种最基本的数据结构,就连LISP 语言的程序也表示为一系列的广义表。
广义表是n(n≥0)个元素a1,a2,…,ai,…,an的有限序列。其中:
ai--或者是原子或者是一个广义表。
广义表通常记作: Ls=( a1,a2,…,ai,…,an)。
Ls是广义表的名字,n为它的长度。
若ai是广义表,则称它为Ls的子表。
此外,广义表具有以下重要的特性:
广义表中的数据元素有相对次序。
广义表的长度定义为最外层包含元素个数。
广义表的深度定义为所含括弧的重数。其中原子的深度为0,空表的深度为1。
广义表可以共享。一个广义表可以为其他广义表共享。这种共享广义表称为再入表。
广义表可以是一个递归的表。一个广义表可以是自已的子表。这种广义表称为递归表。
广义表的存储可以采用多种方式,其中最常见的是线性链表表示法。线性链表表示法中,每个节点包含两部分:tag标记位和hp指针。tag标记位用于区分节点是原子还是子表,通常原子的tag值为0,子表的tag值为1。hp指针用于连接本子表中存储的原子或子表。
另一种常见的存储方式是顺序存储法。这种方法将广义表表示为一维数组,利用数组的索引方便的表示任意位置元素的信息,同时也能够实现表的插入和删除操作。但是这种方法在处理子表时,需要使用到更多的辅助空间。
此外,还有孩子兄弟表示法等其他存储方式。
在C++中,可以使用结构体和指针来存储广义表。下面是一个示例代码,展示了如何使用结构体和指针实现广义表的存储:
#include <iostream> #include <vector> using namespace std; // 定义广义表的结构体 struct GListNode { int tag; // 标记位,用于区分原子和子表 union { int atom; // 原子值 struct GListNode* hp; // 子表头指针 }; vector<GListNode*> cp; // 子表指针数组 }; // 创建广义表节点 GListNode* createNode(int tag, int atom = 0) { GListNode* newNode = new GListNode(); newNode->tag = tag; if (tag == 0) { newNode->atom = atom; } else { newNode->hp = nullptr; } return newNode; } // 插入原子节点 void insertAtom(GListNode*& gl, int atom) { GListNode* newNode = createNode(0, atom); if (gl == nullptr) { gl = newNode; } else { GListNode* p = gl; while (p->cp.size() > 0) { p = p->cp.back(); } p->cp.push_back(newNode); } } // 插入子表节点 void insertSublist(GListNode*& gl, const vector<int>& sublist) { if (gl == nullptr) { gl = createNode(1); } else { GListNode* p = gl; while (p->cp.size() > 0) { p = p->cp.back(); } GListNode* newSublist = createNode(1); for (int atom : sublist) { insertAtom(newSublist, atom); } p->cp.push_back(newSublist); } } // 打印广义表 void printGList(GListNode* gl) { if (gl == nullptr) { cout << "()"; // 空表表示为空括号对 return; } if (gl->tag == 0) { // 原子节点,直接打印原子值并换行 cout << gl->atom << endl; } else { // 子表节点,先打印左括号,然后递归打印子表,最后打印右括号并换行 cout << "("; // 左括号表示子表开始 GListNode* p = gl->hp; // 从子表头开始遍历子表节点 while (p != nullptr) { // 遍历到最后一个子表节点时停止循环,即最后一个子表节点后面没有其他节点了(包括原子节点) printGList(p); // 递归打印子表中的每个节点(包括原子节点和子表节点) p = p->cp.back(); // 移动到下一个子表节点(如果存在的话)或原子节点(如果存在的话)或空(如果已经到达最后一个子表节点的话) } cout << ")"; // 右括号表示子表结束,并换行输出结果(如果当前节点是最后一个子表节点的话)或继续输出下一个原子节点(如果当前节点不是最后一个子表节点的话)或继续输出下一个子表节点(如果当前节点不是最后一个子表节点的话)或结束输出(如果当前节点是空的话) } }
Copyright © 2017-Now pnotes.cn. All Rights Reserved.
编程学习笔记 保留所有权利
MARK:3.0.0.20240214.P35
From 2017.2.6