数据结构(超详细讲解!!)第二十二节 广义表

1.定义

广义表,顾名思义,也是线性表的一种推广。广义表被广泛地应用于人工智能等领域的表处理语言LISP语言中。在LISP语言中,广义表是一种最基本的数据结构,就连LISP 语言的程序也表示为一系列的广义表。

广义表又称列表,是n ( n 大于等于 0 ) 个元素的有限序列,记作A = ( a1, a2, …, an )。其中A是列表的名字,n是它的长度,ai可以是数据元素,也可以是列表。如果ai是列表,则称其为列表A的子表。

习惯上,用大写字母表示列表的名称,用小写字母表示数据元素。用圆括号把列表的元素括起来,用逗号分隔开列表中的元素。

在广义表GL=(d1, d2,d3,…,dn)中,d1是广义表GL的表头,而广义表GL其余部分组成的表(d2,d3,…,dn)称为广义表的表尾。由此可见广义表的定义是递归定义的,因为在定义广义表时又使用了广义表的概念。

广义表是递归定义的线性结构, GL=(d1, d2,d3,…,dn) 其中:di  或为原子或为广义表

数据结构(超详细讲解!!)第二十二节 广义表

广义表是一个多层次的线性结构

数据结构(超详细讲解!!)第二十二节 广义表

列表的深度 :列表展开后的最大括号层次数。

L = (a, b, c)    列表L长度为3,深度为1

E = ( )            E为空表,长度为0,深度为1

A = (x, L, z)   列表A的长度为3,深度为2

B = (A, y, E)  列表B的长度为3,深度为3

C = (A, B)     列表C的长度为2,深度为4

D = (z, D)      列表D的长度为2,深度为无穷大

列表是非终端结点(即交叉结点),数据元素是终端结点,空表作为一个特殊的终端结点。

数据结构(超详细讲解!!)第二十二节 广义表

纯表:通常把与树对应的列表称为纯表,它限制了表中成分的共享性和递归。例如列表L、A、B。

具有共享和递归特性的列表可以和有向图建立对应。

广义表 GL=(d1, d2,d3,…,dn)的结构特点:

1)  广义表中的数据元素有相对次序;

2)  广义表的长度定义为最外层包含元素个数;

3)  广义表的深度定义为所含括弧的重数;    

注意:“原子”的深度为 0               “空表”的深度为 1

4)  广义表可以共享;

5)  广义表可以是一个递归的表。      

递归表的深度是无穷值,长度是有限值。

6)任何一个非空广义表 GL=(d1, d2,d3,…,dn)    均可分解为            

表头  Head(GL) = d1   和   表尾  Tail(GL) = ( d2, …, dn) 两部分。

数据结构(超详细讲解!!)第二十二节 广义表

任何一个非空广义表的表头是表中第一个元素,可以是数据元素,也可以是子表,而其表尾一定是子表。

L = (a, b, c)       E = ( )     A = (x, L, z)      B = (A, y, E)     C = (A, B)      D = (z, D)

head(L) = a,  tail(L) = (b, c)

head(B) = A, tail(B) = (y, E)

head(tail(L)) = head(b, c) = b

tail(tail(tail(L))) = tail(tail(b, c)) = tail(c) = ( )

2.广义表的两种常用存储结构

1)头尾表示法

在广义表的“头尾表示法”中需要两种结点结构:表结点和原子结点。 其中,原子结点是原子元素的存储映像,它包括两个域:标志域和值域; 表结点是子表的存储映像,它包括三个域:标志域、指向表头的指针域和指向表尾的指针域。 标志域为0表示是原子结点,标志域为1表示是表结点。

空表      L=NULL

数据结构(超详细讲解!!)第二十二节 广义表

若表头为原子,则为

数据结构(超详细讲解!!)第二十二节 广义表

否则,依次类推。

数据结构(超详细讲解!!)第二十二节 广义表

/* 广义表的头尾链表存储结构 */
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的联合体域 */
}  *GList; 

数据结构(超详细讲解!!)第二十二节 广义表

2)左孩子右兄弟(子表)表示法

在此种表示法中,同样需要两种结点——表结点和原子结点来分别表示子表和原子元素。将子表中第一个元素(可以是原子元素也可以是子表)称为该子表的左孩子;将元素右边紧挨着它的元素(可以是原子元素也可以是子表)称为该元素的右兄弟。原子结点没有左指针域,但它需要存储原子元素的值。

数据结构(超详细讲解!!)第二十二节 广义表

数据结构(超详细讲解!!)第二十二节 广义表

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;         
} *GList; 

3.基本操作

1.求表头和表尾

//求广义表的表头和表尾。
GList Head(GList L)
{ 
 if(L==NULL) return(NULL);      /* 空表无表头 */ 
 if(L->tag==ATOM) exit(0);    	/* 原子不是表 */ 
 else return(L->atom-htp.htp.hp);
}
GList Tail(GList L)
{ 
  if(L==NULL) return(NULL);    /* 空表无表尾 */ 
  if(L->tag==ATOM) exit(0);     /* 原子不是表*/ 
  else return(L->atom-htp.htp.tp);
} 

2.长度和深度

//求广义表的长度和深度。
int Length(GList L)
{ int k=0; 
 GLNode *s; 
 if(L==NULL) return(0);    		/* 空表长度为0 */ 
 if(L->tag==ATOM) exit(0);    	/* 原子不是表 */ 
 s=L; 
 while(s!=NULL)     		/* 统计最上层表的长度 */
    { k++; 
       s=s->atom-htp.htp.tp;
    } 
 return(k);
}
int Depth(GList L)
{ int d, max=0; 
 GLNode *s; 
 if(L==NULL) return(1);    /* 空表深度为1 */ 
 if(L->tag==ATOM) return(0);    /* 原子深度为0 */ 
 s=L; 
 while(s!=NULL)    /* 求每个子表的深度的最大值 */
   { d=Depth(s->atom-htp.htp.hp); 
      if(d>max) max=d; 
      s=s->atom-htp.htp.tp;
    } 
return(max+1);    /* 表的深度等于最深子表的深度加1 */ 

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/422e38e369.html