广义表简称表,它是线性表的推广。一个广义表是n(n≥0)个元素的一个序列,若n=0时则称为空表。
设ai为广义表的第i个元素,则广义表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) 两部分。
为了简单起见,下面讨论的广义表不包括前面定义的再入表和递归表,即只讨论一般的广义表。
另外,我们规定用小写字母表示原子,用大写字母表示广义表的表名。例如:
A=()E=((a,(a,b),((a,b),c)))
其中A是一个空表,其长度为0;
B是只含有单个原子e的表,其长度为1;
C有两个元素,一个是原子a,另一个是子表,其长度为2;
D有三个元素,每个元素都是一个表,其长度为3;
E中只含有一个元素,是一个表,它的长度为1;
二、广义表的存储结构
广义表是一种递归的数据结构,因此很难为每个广义表分配固定大小的存储空间,所以其存储结构只好采用动态链式结构。
为了使得子表和原子两类节点能在形式上保持一致,又能进行区别,所以采用如下结构。
其中tag域为标志字段;sublist/data由tag决定,为0时表示该节点是原子节点,则第二个域为data,存放原子元素的信息,为1时,表示该节点是表节点,存放sublist,即存放子表第一个元素对应节点的地址。
link域存放与本元素同一层的下一个元素所在节点的地址,当本元素是所在层的最后一个元素时,link域为NULL。
typedef struct lnode
{ int tag; //节点类型标识
union
{ ElemType data;
struct lnode *sublist;
} val;
struct lnode *link; //指向下一个元素
} GLNode; //广义表节点类型定义
三、广义表的运算
(1)求广义表的长度
在广义表中,同一层次的每个节点是通过link域链接起来的,所以可把它看做是由link域链接起来的单链表。这样,求广义表的长度就是求单链表的长度,可以采用以前介绍过的求单链表长度的方法求其长度。
求广义表长度的非递归算法如下:
int GLLength(GLNode *g)
//g为一个广义表头节点的指针
{ int n=0;
g=g->val.sublist; //g指向广义表的第一个元素
while (g!=NULL)
{ n++;
g=g->link;
}
return n;
}
(2)求广义表的深度
对于带头节点的广义表g,广义表深度的递归定义是它等于所有子表中表的最大深度加1。若g为原子,其深度为0。
求广义表深度的递归模型f()如下:
int GLDepth(GLNode *g) //求带头节点的广义表g的深度
{ int max=0,dep;
if (g->tag==0) return 0; //为原子时返回0
g=g->val.sublist; //g指向第一个元素
if (g==NULL) return 1; //为空表时返回1
while (g!=NULL) //遍历表中的每一个元素
{ if (g->tag==1) //元素为子表的情况
{ dep=GLDepth(g); //递归调用求出子表的深度
if (dep>max) max=dep;
//max为同一层所求过的子表中深度的最大值
}
g=g->link; //使g指向下一个元素
}
return(max+1); //返回表的深度
}
(3)输出广义表
以g作为头节点的广义表的表头指针,打印输出该广义表时,需要对子表进行递归调用。
void DispGL(GLNode *g) //g为一个广义表的头节点指针
{ if (g!=NULL) //表不为空判断
{ if (g->tag==1) //为表节点时
{ printf("("); //输出'('
if (g->val.sublist==NULL) printf("");
//输出空子表
else DispGL(g->val.sublist);
//递归输出子表
}
else printf("%c", g->val.data);
//为原子时输出元素值
if (g->tag==1)printf(")");//表节点时输出')'
if (g->link!=NULL)
{ printf(",");
DispGL(g->link);//递归输出后续表的内容
}
}
}
(4)建立广义表
假定广义表中元素类型为ElemType为char类型,每个原子的值被限定为单个英文字母。并假设广义表是一个正确的表达式,其格式为:元素之间用一个逗号分隔,表元素的起止符号为左、右括号,空表在元括号内包含#。
算法如下:
GLNode *CreateGL(char *&s)
{
GLNode *g;
char ch=*s++;//取一个字符
if(ch!='\0') //读取串
{ g=(GLNode *)malloc(sizeof(GLNode));
if(ch=='(')//左括号
{ g->tag=1;
g->val.sublist=CreateGL(s);//构造子表并链接到表头
}
else if(ch==')')//右括号
g=NULL;
else if(ch=='#')//空表
g=NULL;
else
{ g->tag=0;//原子字符
g->val.data=ch;
}
}
else
g=NULL;//串结束
ch=*s++;//下一个字符
if(g!=NULL)//如果串未结束,继续构造兄弟节点
if(ch==',')
g->link=CreateGL(s);
else
g->link=NULL;
return 0;
}