首页  /  数据结构教程  /  广义表(一)  /  

广义表(一)

点击打开在线编译器,边学边练

1.    简介

数组可以存储不允许再分割的数据元素,如字符’X’,数字11,当然他也可以存储数组,二维数组就是一个例子,你可以理解二维数组的每一行的元素是一列中的对应元素的组合。

广义表是一种线性表,或者说,广义表是一种线性表的推广,它属于多层次的线性表,广义表中可以存储不可以再分割的元素,同时也可以存储一张广义表(子表),因此适用情况相对灵活。

设ai为广义表的第i个元素,广义表是n(n≥0)个元素的一个序列,若n=0时则称为空表。,广义表GL的一般表示与线性表相同,即:

GL=(a1,a2,…,ai,…,an)

其中n表示广义表的长度,即广义表中所含元素的个数,n≥0。如果ai是单个数据元素,则ai是广义表GL的原子;如果ai是一个广义表,则ai是广义表GL的子表。

 

一般来说,广义表具有如下重要的特性:

(1)广义表中的数据元素有相对次序

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

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

(4)广义表可以共享;一个广义表可以为其他广义表共享;这种共享广义表称为再入表

(5)广义表可以是一个递归的表。一个广义表可以是自已的子表。这种广义表称为递归表。递归表的深度是无穷值,长度是有限值

(6)任何一个非空广义表GL均可分解为表头head(GL) = a1和表尾tail(GL) = ( a2,…,an) 两部分。 

 

2.    广义表的结点设计

我们以常规方法来看,广义表是一种不定规模的数据结构,很难为之分配具体的空间,因此创建的方法采用动态的链式方法,动态的创建空间。

如图:

2222.png

        对于每一个结点而言由如上三大部分组成,其中Tag域为标志字段,其只有两个参数,0或者1(Tag域使用int类型,在某些情况下因为只需要简单判断也可以使用更短的类型,如bool);Atom/Node域的内容由tag标志决定,当Tag为0时表示该节点是原子结点(即存放原子数据),当Tag为1时表示该节点为指向下一个广义表的指针(即表结点),Link域存放与本元素同一层的下一个元素所在的结点地址,当本元素时所在层的最后一个元素时,Link域为NULL;

        链式法设计:

#define AtomType int
typedef enum{ATOM,LIST}ElemTag; //ATOM = 0:原子;LIST = 1:子表 
/*结点设计*/
typedef struct GLNode{
    ElemTag tag; //枚举类型的标志域,只能取定义了的枚举值
    union{   //union联合体,下面两部分只能取其一;要么取AtomType;要么取结构体ptr,ptr包括两个指针hp,tp 
        AtomType atom;
        struct{
            struct GLNode *hp,*tp;
        }ptr;
    }; 
}*GList; //定义广义表类型,GList为指针

下面是子表法设计:

/*线性表存储之扩展线性表 = 子表法*/ 
typedef struct GLNode{
    ElemTag tag;
    union{
        AtomType atom;
        struct GLNode *hp;    //对于列表,hp指向本列表内部第一个元素,而tp是指向本层次上的下一个元素 
    };                        
    struct GLNode *tp;
} *GList;

        首先定义AtomType的数据类型,可以为基本的int型,也可以为一些其他的数据类型,包括简单的char或者是复杂一些的结构体,不过这里不建议创建过于复杂的结构体。

其次,关于tag域的建立,我们可以使用一个枚举建立,分别表示Atom/Node到底取何种值,而关于Atom/Node域,则可以建立一个union(共用体)来表示,共用体公用内存空间,只能取其一赋值,这也符合广义表的基本需求;而关于表达的Link域,我们使用链式的存储方法可以化简,而使用子表的方式则必须表达。


上一课:矩阵的扩展算法 下一课:广义表(二)
第一章 数据结构入门
第二章 链表
第三章 栈
第四章 队列
第五章 从C语言到C++
第六章 串,数组,矩阵,广义表
第七章 树
第八章 图
第九章 算法—查找
第十章 算法—排序
第十一章 算法&竞赛,思维培养
第十二章 后记