数据结构与C++

数据结构与C++ 知识量:9 - 32 - 91

5.3 广义表><

广义表的定义- 5.3.1 -

广义表(Lists,又称列表)是一种非线性的数据结构,是线性表的一种推广。即广义表中放松对表元素的原子限制,容许它们具有其自身结构。它被广泛的应用于人工智能等领域的表处理语言LISP语言中。在LISP语言中,广义表是一种最基本的数据结构,就连LISP 语言的程序也表示为一系列的广义表。

广义表是n(n≥0)个元素a1,a2,…,ai,…,an的有限序列。其中:

  • ai--或者是原子或者是一个广义表。

  • 广义表通常记作: Ls=( a1,a2,…,ai,…,an)。

  • Ls是广义表的名字,n为它的长度。

  • 若ai是广义表,则称它为Ls的子表。

此外,广义表具有以下重要的特性:

  1. 广义表中的数据元素有相对次序。

  2. 广义表的长度定义为最外层包含元素个数。

  3. 广义表的深度定义为所含括弧的重数。其中原子的深度为0,空表的深度为1。

  4. 广义表可以共享。一个广义表可以为其他广义表共享。这种共享广义表称为再入表。

  5. 广义表可以是一个递归的表。一个广义表可以是自已的子表。这种广义表称为递归表。

广义表的存储- 5.3.2 -

广义表的存储可以采用多种方式,其中最常见的是线性链表表示法。线性链表表示法中,每个节点包含两部分: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 << ")"; // 右括号表示子表结束,并换行输出结果(如果当前节点是最后一个子表节点的话)或继续输出下一个原子节点(如果当前节点不是最后一个子表节点的话)或继续输出下一个子表节点(如果当前节点不是最后一个子表节点的话)或结束输出(如果当前节点是空的话)  
    }  
}