一 广义表的定义与性质
1.广义表的定义
广义表的定义:广义表是 n ( n ≥ 0 ) n\ (n≥0)n (n≥0) 个表元素组成的有限序列。
广义表的表示:L = {a1,a2,...,ai,....an}
其中L是广义表的名称,n是广义表的长度,每个ai都是L的成员,它可以是单个元素,也可以是一个广义表。由此可见广义表的定义是递归的。
————————————————
广义表通常用圆括号括起来,并用逗号分隔表中元素。
A=()——A是一个空表,长度为0。
B=(e)——B只有一个原子e,长度为1。
C=(a,(b,c,d))——列表C长度为2,两个元素分别是原子a于子表(b,c,d)
D=(a,D)——这是一个递归的表,长度为2,相当于D=(a,(a,(a,...)))。
2.广义表的性质
我们可以从以上定义以及例子中得到以下重要性质
1.广义表是一种多层次的数据结构,其中元素可以是单元素,也可以是子表。
2.广义表可以是递归的表,即广义表也可以是自身的子表。例如表D就是递归的表。
3.广义表可以为其他表所共享
根据以上的定义性质可以得到以下结论:
- 有次序:广义表的表元素的排列次序不能随意交换
- 有层次:广义表的表元素可以是子表,子表还可以有子表
- 有深度:最大嵌套层数即为广义表的深度,用括号重数来识别
- 可共享:广义表的子表可为多个广义表的子表
- 可递归:广义表的子表可以是自身
二 广义表的存储
1.头尾表示法
由于广义表中数据元素即可能是表也可能是单元素,相应地在头尾表示法中节点有两种形式:一种是元素节点,用以表示原子,一种是表节点,用以表示表。
表示原子的节点由两部分构成,分别是 tag =0标记位和原子的值,
表示子表的节点由三部分构成,分别是 tag =1标记位、hp 指针和 tp 指针。
typedef struct GLNode
{
int tag; // 标志域, 用于区分原子节点与表节点,0表示原子, 1表示子表
union
{
DataType atom; // atom原子结点的值域,DataType由用户定义。
struct
{
struct GLNode * hp, *tp;
}ptr; // 子表结点的指针域, ptr.hp指向表头, ptr.tp指向表尾
};
}*Glist;
在这种存储情况中有以下几种情况:
(1)除空表的表头指针为空外,对任何非空列表,其表头指针均指向一个表节点,且该节点重点hp指针指向列表表头。(或为原子节点或为表节点),tp指针指向列表表尾(除非表尾为空,则指针为空,否则必为表节点);
(2)容易分清列表中原子和列表所在层此。
(3)最高层的表节点个数即为列表长度。
2.孩子兄弟表示法
广义表的另一种表示法称为孩子兄弟表示法。在孩子兄弟表示法中,也有两种节点形式:一种是有孩子节点,用以表示子表;另一种是无孩子节点,用以表示原子,在有孩子节点中包括一个指向第一个孩子的指针和一个指向兄弟的指针;而在无孩子节点中包括一个指向兄弟的指针和该原子的元素值。
为了区分这两类节点,在节点中设置一个标志域。若标志域为1则为有孩子节点;反之则为无孩子节点。
typedef struct GLNode
{
int tag; // 标志域, 用于区分原子节点与表节点,0表示原子, 1表示子表
union
{
DataType atom //原子节点的值域
struct GLNode * hp; //表节点的表头指针
};
struct GLNode *tp; //相当于线性链表的next,指向下一个元素节点
}*Glist;