【数据结构】广义表

一、广义表的定义

  

     广义表简称表,它是线性表的推广。一个广义表是n(n>=0)个元素的一个有限序列,当n=0时称为空表。在一个非空的广义表中,其元素可以是某一确定类型的对象,这种元素被称为单元素;也可以是由单元素构成的表,这种元素被称为子表或表元素。显然,广义表的定义是递归的,广义表是线性表的递归数据结构。

        社ai为广义表的第i个元素,则广义表的一般表示与线性表相同。

        (a1,a2,a3,a4......,an)

        其中,n表示广义表的长度,即广义表中所含元素的个数,n>=0。

        同线性表一样,也可以用一个标识符来命名一个广义表,例如:用LS命名上面的广义表,则为:LS(a1,a2,a3...an+1,an)。在广义表的讨论中,为了把单元素同表元素区别开来,一般用小写字母表示表元素,用大写字母表示表,例如:A=()

                                           B=(e)

                                           C=(a,(b,c,d))

                                           D=(A,B,C)=((),(e),(a,(b,c,d)))

                                           E=((a,(a,b),((a,b),c)))

       其中,A是一个空表,不含任何元素,其长度为0;B是一个只含有单元素e的表,其长度为1;C中有两个元素,第1个元素是单元素a,第2个元素是表元素(a,b,c),C的长度为2;D中有3个元素,其中每个元素又都是一个表,D的长度为3;E中只含有一个元素,该元素是一个表,该表中含3个元素,其中后两个元素又都是表。

        一个广义表的深度是指该表中括号嵌套的最大重数,在图像中则是值从树根节点到每个表元素结点所经过的结点个数的最大值。


二、广义表的存储结构

        广义表是一种递归的数据结构,因此很难为每个广义表分配固定大小的存储空间,所以其存储结构只好采用动态链接结构。

        广义表中的结点类型可定义为:

  1. public class GeneralizedNode { //假定用GeneralizedNode来表示广义表中的结点类
  2. boolean tag; //结点标志域
  3. Object data; //结点值域
  4. GeneralizedNode sublist; //指向子表的引用域
  5. public GeneralizedNode(boolean tag, Object data, GeneralizedNode sublist) {
  6. this.tag = tag;
  7. this.data = data;
  8. this.sublist = sublist; //利用每个参数分别给相应成员赋值
  9. }
  10. }

       当一个结点的标志域tag的取值为false时,表明它是一个单元素结点,此时结点值域data和后继链接域next有效,而子表的链接(表头指针)域sublist无效;相反,当tag域的取值为true时,则表明它是一个表元素(子表)结点,此时指向子表的链接域sublist和指向后继结点的链接域next有效,而结点的值域data无效。在广义表的链接存储结构中,通过表结点中的sublist域,保存其子表中第1个元素结点的引用,实现向下一层子表的链接;通过每个结点中的next域,保存其后继结点的引用,实现向同层次的后继结点的链接。


三、广义表类的定义

        在广义表类的定义中,应包含数据成员和方法成员,数据成员一般是私有的,以体现类和对象的隐藏性,方法成员若被被提供作为外部调用的接口,则应是非私有的,若只提供给内部的方法所调用,则应是私有的。

       广义表是线性表的递归结构,它的链接存储结构也是单链表的递归结构,所有对它进行的各种运算方法必然也是递归的。

       当类中的一个方法需要递归实现时,必须另外定义一个驱动方法,该方法是一个非递归方法,方法体中只包含一条调用递归方法的语句。驱动方法的功能有两个:一是提供给外部的类和对象调用,所有它通常具有公有的访问属性;二是调用同名的递归方法,实现运算的功能,递归方法只需要提供给本类使用,不需要提供给外部使用,所有通常被定义为私有访问属性。

3.1 求广义表的长度

      在广义表 中,同一层次的每个结点是通过next域链接起来的,所有它是由next域链接起来的单链表。这样,求广义表的长度就是求单链表的长度,可以采用重复循环,从头到尾依次访问单链表中的每个结点,并进行已访问结点个数的统计,则能够求出单链表的长度,亦即广义表的长度。

  1. private int length(GeneralizedNode glt) //求广义表长度的递归函数
  2. { //求出表头指针glt所指向的广义表的长度,glt初始指向广义表的第1个结点
  3. if(glt!= null) //若glt不为空,其长度等于1加上后继表的长度
  4. {
  5. return 1+length(glt.next);
  6. }
  7. else //若glt为空,其长度等于0
  8. return 0;
  9. }

3.2 求广义表的深度

       广义表深度的递归定义是它等于所有子表中表的最大深度加1。若一个表为空或仅由单元素所组成,则深度为1,它是结束向下继续递归求深度的终止条件。设dep表示任一子表的深度,max表示所有子表中的最大深度,depth表示广义表的深度,则有depth=max+1

       当一个表不包含任何子表时,其深度为1,所以在算法中max的初值应为0,返回max+1的值1就正好等于此时表的深度。

  1. private int depth(GeneralizedNode glt) //求广义表深度的递归函数
  2. { //求出表头指针glt所指向的广义表的深度,glt初始指向广义表的第1个结点
  3. int max= 0; //给max赋初值0
  4. while(glt!= null) //遍历表中每一个结点
  5. {
  6. if(glt.tag== true)
  7. {
  8. int dep=depth(glt.sublist); //递归调用求出子表的深度
  9. if(dep>max)
  10. max=dep; //让max为同层求过子表深度的最大值
  11. }
  12. glt=glt.next; //使表头指针指向同一层的下一个结点
  13. }
  14. return max+ 1;
  15. }

     下面给出广义表类的具体定义:

  1. public class GeneralizedList { //广义表类的定义
  2. private GeneralizedNode head; //广义表结点引用域
  3. private int i= 0; //此变量i为建立广义表create方法所使用
  4. public GeneralizedList() //广义表的构造方法,创建一个空的表结点
  5. { //表结点的tag域为true,其余为空值
  6. head= new GeneralizedNode( true, null, null, null);
  7. }
  8. public int length() //求广义表长度的驱动方法
  9. {
  10. return length(head.sublist); //以表结点的子表引用域为参数调用递归函数
  11. }
  12. private int length(GeneralizedNode glt) //求广义表长度的递归函数
  13. { //求出表头指针glt所指向的广义表的长度,glt初始指向广义表的第1个结点
  14. if(glt!= null) //若glt不为空,其长度等于1加上后继表的长度
  15. {
  16. return 1+length(glt.next);
  17. }
  18. else //若glt为空,其长度等于0
  19. return 0;
  20. }
  21. public int depth() //求广义表深度的驱动方法
  22. {
  23. return depth(head.sublist); //以表结点的子表引用域为参数调用递归函数
  24. }
  25. private int depth(GeneralizedNode glt) //求广义表深度的递归函数
  26. { //求出表头指针glt所指向的广义表的深度,glt初始指向广义表的第1个结点
  27. int max= 0; //给max赋初值0
  28. while(glt!= null) //遍历表中每一个结点
  29. {
  30. if(glt.tag== true)
  31. {
  32. int dep=depth(glt.sublist); //递归调用求出子表的深度
  33. if(dep>max)
  34. max=dep; //让max为同层求过子表深度的最大值
  35. }
  36. glt=glt.next; //使表头指针指向同一层的下一个结点
  37. }
  38. return max+ 1;
  39. }
  40. public Object find(Object obj) //从当前广义表中查找值为obj的结点
  41. {
  42. return find(head,obj); //此为调用递归函数的驱动方法
  43. }
  44. //以整个广义表结点的引用和obj作为参数
  45. private Object find(GeneralizedNode glt,Object obj)
  46. { //从当前广义表中查找值为obj的递归方法,glt初始指向整个广义表的表结点
  47. while(glt!= null) //处理广义表中的每个结点
  48. {
  49. if(glt.tag== false) //处理单元素结点
  50. {
  51. if(glt.data.equals(obj))
  52. {
  53. return glt.data; //查找成功返回结点值
  54. }
  55. else
  56. glt=glt.next; //否则继续向后继结点查找
  57. }
  58. else //处理表元素结点
  59. {
  60. Object x=find(glt.sublist,obj); //在子表中查找,结果存入x中
  61. if(x!= null)
  62. {
  63. return x; //在子表中查找成功返回结点值
  64. }
  65. else
  66. {
  67. glt=glt.next; //否则继续向表结点的后继结点查找
  68. }
  69. }
  70. }
  71. return null; //没有从表中找到应返回空值
  72. }
  73. public void output() //输出当前广义表的驱动方法
  74. {
  75. output(head); //以整个广义表结点的引用作为调用实参
  76. }
  77. private void output(GeneralizedNode glt)//输出广义表的递归函数
  78. {
  79. //以广义表的书写格式输出广义表glt,glt初始指向整个广义表的表结点
  80. if(glt.tag== true) //对表结点进行处理
  81. {
  82. System.out.print( "("); //先输出左括号,作为表的开始符号
  83. if(glt.sublist== null) //若子表引用为空,则输出‘#’字符表示空表
  84. {
  85. System.out.print( "#");
  86. }
  87. else //当一个子表输出结束后,应输出右括号作为终止符
  88. output(glt.sublist);
  89. System.out.print( ")"); //对于单元素结点,输出该结点的值
  90. if(glt.next!= null) //处理后继表
  91. {
  92. System.out.print( ","); //先输出逗号分隔符
  93. output(glt.next); //再递归输出后继表
  94. }
  95. }
  96. }
  97. public void create(String s) //根据字符串s中保存的广义表建立其存储结构
  98. {
  99. create(head,s); //此为调用同名递归函数的驱动方法
  100. }
  101. private void create(GeneralizedNode glt,String s)
  102. { //以整个广义表的引用和字符串s作为参数
  103. //建立广义表存储结构的递归函数,glt初始指向整个广义表的表结点
  104. char ch= '\0';
  105. //利用ch读取一个字符,循环结束后ch读取的只可能是左括号、英文字母或#字符
  106. while((ch=s.charAt(i))== ' ')
  107. {
  108. i++; //忽略掉空格
  109. }
  110. //若读取的为左括号则建立由glt.sublist所指向的子表并递归构造子表
  111. if(ch== '(')
  112. {
  113. glt.sublist= new GeneralizedNode( true, null, null, null); //初始为表结点
  114. i++;
  115. create(glt.sublist,s); //递归建立子表
  116. if(s.charAt(i)== '#') //若子表为空表,则修改子表域为空
  117. {
  118. while((ch=s.charAt(++i))== ' ') //忽略空格
  119. {
  120. if(ch== ')')
  121. {
  122. glt.sublist= null; //修改子表域为空
  123. }
  124. else
  125. {
  126. illegalChar(); //处理非法字符
  127. }
  128. }
  129. }
  130. //若读取的为字符元素,则修改初始建立的表结点为现在的单元素结点
  131. else if(ch>= 'a' && ch<= 'z')
  132. {
  133. glt.tag= false; //修改为单元素结点
  134. String str = String.valueOf(ch);
  135. glt.data=str; //给数据域赋值
  136. }
  137. //若读取的为表示空的井字符,则返回到上面的递归建立子表的语句之后
  138. else if(ch== '#') return;
  139. //ch所读取的字符必为非法字符,应显示错误信息并退出运行
  140. else illegalChar();
  141. i++; //处理一个子表或元素后,使指示处理字符位置的i值增1
  142. //空格被跳过,循环结束后,ch读取的字符必为逗号、右括号或分号
  143. while((ch=s.charAt(i))== ' ')
  144. {
  145. i++; //忽略掉空格
  146. }
  147. //若读取的为逗号则递归构造后继表
  148. if(ch== ',')
  149. {
  150. glt.next= new GeneralizedNode( true, null, null, null); //初始为表结构
  151. i++; //使指示处理字符位置的i值增1,以便处理下一个子表或单元素
  152. create(glt.next,s); //递归调用构造后继表
  153. }
  154. //若读取的为右括号或分号表明相应的子表或整个表处理完毕,则不做任何事情,自然结束
  155. else if(ch== ')'||ch== ';');
  156. //所读取的字符必为非法字符,应显示错误信息并退出运行
  157. else illegalChar();
  158. }
  159. }
  160. private void illegalChar() //在create算法中,调用此方法处理非法字符
  161. {
  162. System.out.println( "广义表字符串中存在非法字符,终止运行!");
  163. System.exit( 1);
  164. }
  165. public void clear() //清除广义表中的所有元素,使之成为空表
  166. {
  167. head.sublist=head.next= null;
  168. }
  169. }

猜你喜欢

转载自blog.csdn.net/hellozex/article/details/81036760