广义表
与数组一样,广义表也是线性表的推广。它是一种递归定义的数据结构,但其数据元素的结构类型可以不同,其元素可以是普通元素,也可以是广义表。广义表被广泛应用于人工智能等领域的表处理语言Lisp中它把广义表作为基本的数据结构,就连程序也表示成一系列的广义表。
【广义表的定义及头尾链表表示】
广义表,简称表(lists),是由n个元素(a1,a2,a3,…,an)组成的有限序列。当n=0时称为空表。在一个非空的广义表中,其元素ai可以是某一确定类型的对象(这种元素被称为单元素或原子),也可以是由原子构成的表(这种元素被称为子表或表元素)。显然,广义表的定义是递归的。广义表记作(a1,a2,a3,.... ,an)。其中,GL是广义表的名字,n是广义表的长度。在广义表GL中,a1称为广义表GL的表头(head),其余元素组成的表(a2,a3,...,an)称为表尾(tail)。
例如:
(1)A=0,广义表A是长度为0的空表。
(2)B=(a),B是一个长度为1且元素为原子的广义表(其实就是前面讨论过的一般的线性表)。
(3)C=(a,(b,c)),C是长度为2的广义表。其中,第1个元素是原子a,第2 个元素是一个子表(b,c)。
(4)D=(A,B,C),D 是一个长度为3的广义,这3个元素都是子表,第1个元素是一个空表A。
(5)E=(a,E),E是一个长度2的递归广义表,相当于E=(a,(a,(a,(a,(a, ...)))))。任何一个非空广义表的表头可以是一个原子,也可以是一个广义表,而表尾一定是一个广义表。例如:
L=(a,(b,(c,(d)), e), f )
⑴ L1=Tail(L)=((b,(c,(d)), e), f )
⑵ L2=Head(L1)= (b,(c,(d)), e)
⑶ L3=Tail(L2)=((c,(d)), e)
⑷ L4=Head(L3)=(c,(d))
⑸ L5=Head(L4)= c
习惯上,广义表的名字用大写字母表示,原子用小写字母表示。
【性质】
(1)有次序性。广义表中的元素有固定的相对次序。从某种程度上,广义表是线性排列的,可以把广义表看成是线性表的推广。同时,它又是层次结构,可以把它看成树的推广。
(2)有长度。广义表的长度定义为最外层括弧中包含的数据元素的个数。表中的元素个数是有限的,也可以是空表。
(3)有深度。广义表的深度定义为括弧的最大重数,空表的深度为1。
(4)可递归。
(5)可共享。即子表可被多个广义表共享。
【存储结构】
由于广义表中的元素可以是原子,也可以是广义表,显然难以用顺序存储结构表示。广义表中有两种元素:原子和子表,因此需要两种结构的结点:一种是原子结点,用来表示原子;另一种是表结点,用来表示表元素。一般用链式方式表示广义表,有两种存储方式:头尾链表存储结构和扩展线性链表存储结构。
其中头尾链表存储结构由两种结点构成:原子结点和表结点。其中,表结点包含3个域:标志域,指向表头的指针域和指向表尾的指针域。原子结点包含两个域:标志域和值域。表结点和原子结点原子结点的存储结构
如图所示。
其中,tag=1是子表,hp和tp分别指向表头结点和表尾结点,tag=0表示原子,atom用于存储原子的值。
用头尾链法表示广义表A=(),B=(a),C=(a,(b,c)),D=(A,B,C),E=(a,E)
广义表的头尾链表存储结构类型描述如下:
typedef enum{ATOM,LIST}ElemTag; /*ATOM=0,表示原子,LIST=1,表示子表*/
typedef struct
{
ElemTag tag; /*标志位tag用于区分元素是原子还是子表*/
union
{
AtomType atom; /*AtomType是原子结点的值域,用户自己定义类型*/
struct
{
struct GLNode *hp,*tp; /*hp指向表头,tp指向表尾*/
}ptr;
};
}*GList,GLNode;