前提条件
广义表的基本概念
- 将线性表的元素定义进行扩展,每个元素也可以是一个表,得到一种“表中有表”的递归数据结构,称为广义表。
- 例如,在常用的操作系统中,文件系统(File System)的组织结构是一个典型的广义表应用。
- 在某个文件夹下,既可能存在文件,也可能存在文件夹。如下图是C:\Program Files\Wandoujia文件夹的情况。Wandoujia文件夹是一个广义表,从图左边的树形组织结构可以看出,其包含了3个子文件夹:App、CrashReports和 Update,它们也都是广义表。在图右边展开的Update文件夹包含了4个子文件夹和1个文件:1.1.0.3、Download 、Install、Offiline和 wandoujia_update.exe ,而Download文件夹继续包含了其他文件夹,形成了表中有表的结构。

- 广义表(Generalized List)一般记作
L
=
(
α
1
,
α
2
,
.
.
.
,
α
n
)
L=(α_1,α_2,...,α_n)
L=(α1,α2,...,αn)
- 其中,
α
i
α_i
αi既可以是不再细分的元素,也可以是广义表,分别称为广义表L的原子和子表。
- 表头(Head)和表尾(Tail):对于非空广义表,第一个元素
α
1
α_1
α1构成了L的表头。表头可能是原子,也有可能是表。陈表头之外的元素序列
(
α
2
,
.
.
.
,
α
n
)
(α_2,...,α_n)
(α2,...,αn),构成了L的表尾,表尾必定是一个广义表。
- 长度(Length):广义表的元素个数。空表的长度为0。
- 深度(Depth):广义表中括弧的最大嵌套层数。空表的深度为1,原子的深度为0.
下图是一组广义表示例。其中,L5共享了L1、L3和 L4,L6是一个无穷深度的列表
a
,
(
a
,
(
a
,
…
)
)
)
a,(a, (a,…)))
a,(a,(a,…)))。

广义表的存储结构
- 由于广义表的每个元素既可以是原子,又可以是表,导致不能统一规定每个元素所需的存储空间。如果采用顺序存储结构,则无法在初始化时确定所需要的内存空间,因此,广义表通常采用链式存储结构,以便于进行存储空间的管理。
- 广义表的结点分为两种,分别称为原子结点和表结点。表结点用于表示一个广义表,含有值为LIST的标志域tag、指向表头的指针域hp和指向表尾的指针域tp。原子结点用于表示一个原子,含有值为ATOM的标志域tag和存放原子的值域atom,如图所示。
- 表结构
- 对于广义表
L
5
=
(
(
)
,
(
a
)
,
(
b
,
(
c
,
(
d
)
,
e
)
)
)
L5=((),(a),(b,(c,(d),e)))
L5=((),(a),(b,(c,(d),e))),结构示意图如下。

- 注:0为原子结点,1为表结点。
存储结构类型定义
typedef char AtomType;
typedef enum {
ATOM,LIST
}ElemTag;
typedef struct GLNode{
ElemTag tag;
union{
AtomType atom;
struct{
struct GLNode *hp;
struct GLNode *tp;
}ptr;
}un;
}GLNode.*GList;
广义表的分解方式
方式一:按表头和表尾分解
方式二:按元素分解
- 以
L
=
(
f
,
(
g
)
,
h
)
为
例
L=(f,(g),h)为例
L=(f,(g),h)为例

- 注:方式二比方式一的分解层数少。方式一每次只将问题规模分为1和n-1,而方式二则将问题规模划分成n个子问题,每个子问题的规模为1。
广义表的基本操作
求广义表深度
- 基本思路:依据分解方式一,将广义表划分成表头和表尾。分别求出表头深度h1和表尾深度h2,然后作比较。
- (1)如果是空表或者原子,则直接求得深度1或0。
- (2)如果是非空表,则分别对表头和表尾递归求解。
- (3)广义表的深度是表头深度加1和表尾深度较大值。
int GListDepth(GList L) {
if(NULL==L) return 1;
if(ATOM==L->tag) return 0;
h1=GListDepth(L->un.ptr.hp)+1;
h2=GListDepth(L->un.ptr.tp);
return h1>=h2?h1:h2;
}
在表尾添加元素
- 该操作可基于分解方式二确定广义表的最后一个元素位置,在此位置后添加新的表结点,并将参数p赋予其表头指针hp。

Status Append(GList &L,GLNode *p){
GList tail = (GList)malloc(sizeof(GLNode));
if(NULL==tail) return OVERFLOW;
tail->tag = LIST;
tail->un.ptr.hp = p;
tail->un.ptr.tp = NULL;
if(NULL==L) L = tail;
else{
for(GLNode *pp=L; pp->un.ptr.tp!=NULL;pp=pp>un.ptr.tp);
pp->un.ptr.tp=tail;
}
return OK;
}
参考文献
[1] 严蔚敏,吴伟民. 数据结构(C语言版). 北京: 清华大学出版社,2020
[2] 严蔚敏,李冬梅,吴伟民. 数据结构(C语言版)(第二版). 北京: 人民邮电出版社,2021
[3] 吴伟民,李小妹,刘添添,黄剑锋,苏庆,林志毅,李杨.数据结构. 北京:高等教育出版社,2017
[4] 王道论坛. 2022数据结构考研复习指导. 北京:电子工业出版社,2021