5.5广义表
广义表,也是线性表的一种推广。它被广泛的应用于人工智能等领域的表处 理语言 LISP 语言中。在 LISP 语言中,广义表是一种最基本的数据结构,LISP 语言 编写的程序也表示为一系列的广义表。
一、 广义表的概念
在第 2 章中,线性表被定义为一个有限的序列( a1 ,a2 ,a3 ,…, an),其中 ai被限定 为是单个数据元素。
广义表也是 n 个数据元素( d1 ,d2 ,d3 ,…, dn)的有限序列,但不 同的是,广义表中的 di 既可以是单个元素,还可以是一个广义表,通常记做: GL =( d1 ,d2 ,d3 ,…, dn)。
GL 是广义表的名字,通常广义表的名字用大写字母表示。
n 是广义表长度。
若其中 di是一个广义表,则称 di是广义表 GL 的子表。
在广义表 GL中,d1是广义表表头,而广义表GL其余部分组成的表( d2 ,d3 ,…, dn)称为广义表表尾。
由此可见广义表的定义是递归定义的,因为在定义广义表时又使用了广义 表的概念。
下面给出一些广义表的例子,以加深对广义表概念的理解。
- D=() 空表;其长度为零。
- A=(a,(b,c)) 表长度为 2 的广义表,其中第一个元素是单个数据 a, 第二个元素是一个子表(b,c)。
- B=(A,A,D) 长度为 3 的广义表,其前两个元素为表 A,第三个元素 为空表 D。
- C=(a,C) 长度为 2 递归定义的广义表,C 相当于无穷表 C=(a,(a, (a,(…))))。
其中,A、B、C、D 是广义表的名字。
下面以广义表 A 为例,说明求表头、表尾的操作。
head(A)= a;表 A 的表头是: a
tail(A)=((b,c));表 A 的表尾是((b,c))。广义表的表尾一定是一个表。
从上面的例子可以看出:
- ( 1 )广义表的元素可以是子表,而子表还可以是子表……,因此,广义表是一个 多层的结构。
- ( 2 )广义表可以被其他广义表共享。如广义表 B 就共享表 A。在表 B 中不必 列出表 A 的内容,只要通过子表的名称就可以引用该表。
- ( 3 )广义表具有递归性,如广义表 C。
二、 广义表的存储结构
由于广义表 GL=( d1 ,d2 ,d3 ,…, dn)中的数据元素既可以是单个元素,也可以是 子表,因此对于广义表来说,难以用顺序存储结构来表示它,通常用链式存储结构来表示。
1 .广义表的头尾链表存储结构
广义表中的每个元素用一个结点来表示,表中有两类结点:
一类是单个元素结 点,另一类是子表结点。
任何一个非空的广义表都可以将其分解成表头和表尾两部分,反之,一对确定的表头和表尾可以惟一地确定一个广义表。
因此,一个表结点可由三个域构成:标志域、指向表头的指针域和指向表尾的指针域,而元素结点只需要两个域:标志域和值域,其结点结构如下图所示。
广义表的头尾链表存储结构类型定义如下:
typedef enum {ATOM, LIST} ElemTag; /* ATOM=0,表示原子;LIST=1, 表示子表*/
typedef struct GLNode
{
ElemTag tag; /*标志位 tag 用来区别原子结点和表结点 */
union
{
AtomType atom; /*原子结点的值域 atom*/
struct
{
struct GLNode * hp, *tp;
}htp; /*表结点的指针域 htp, 包括 表头指针域hp和表尾指针 域 tp*/
}atom_htp; /* atom_htp 是原子结点的值域 atom 和 表结点的指针域 htp 的联合体域*/
} GLNode,*GList;
前面提到的广义表 A、B、C、D 的存储结构如下图所示:
在这种存储结构中,能够很清楚地分清单元素和子表所在的层次,在某种程度 上给广义表的操作带来了方便。
2 .广义表的同层结点链存储结构
在这种结构中,无论是单元素结点还是子表结点均由三个域构成。其结点结 构如下图所示。
广义表 A、 B、 C、 D 的扩展线性链表存储结构如下图所示
广义表的同层结点链存储结构类型定义如下:
typedef enum {ATOM,LIST} ElemTag; /* ATOM=0,表示原子;LIST=1,表 示子表*/
typedef struct GLNode
{
ElemTag tag;
union
{
AtomType atom;
struct GLNode * hp; /*表头指针域*/
}atom_hp; /* atom_hp 是原子结点的值域 atom 和 表结点的表头指针域 hp 的联合体域*/
struct GLNode * tp; /*同层下一个结点的指针域*/
}GLNode,*GList;