广义表的定义:
广义表(Generalized List)是一种数据结构,它是线性表的推广。线性表是由零个或多个元素组成的有限序列,而广义表可以包含原子元素(即非广义表的元素)和子表(即广义表),这使得广义表能够表示更加复杂的数据结构。
广义表(Lists,又称列表)是一种非连续性的数据结构,是线性表的一种推广。在线性表的定义中,a[i]只限于单个元素,而在广义表中,a[i]不仅可以是单个元素,还可以是另一个广义表,从而放松了对表元素的原子限制,容许它们具有其自身的结构。因此,广义表是由零个或多个单元素或子表所组成的有限序列。通常用大写字母表示广义表的子表,用小写字母表示原子。当广义表非空时,称第一个元素a[i]为表头,称其余元素组成的表(a[2],a[3],a[4],…… ,a[n])为表尾。
广义表具有多种性质:
递归结构:广义表可以包含其他广义表作为其元素,形成多层次的嵌套结构。
可变长度:广义表是一个可变长度的数据结构,它可以包含零个或多个元素。
不唯一性:两个广义表可能具有相同的元素,但它们的顺序和嵌套结构可能不同。
灵活性:广义表支持插入、删除、查找等操作,可以动态增长或缩小。
广义表通常使用链式存储结构来存储数据,但也可以使用数组等其他存储结构来实现。这些数据通常具有嵌套和层次结构,而广义表可以很好地表示这些特性
广义表语法:
广义表的语法通常遵循一定的规则来表示其结构和内容。以下是一些关于广义表语法的关键要点:
表示方法:
广义表通常使用圆括号 () 来括起其元素,并使用逗号 , 来分隔不同的元素。
为了区分原子(基本类型的数据)和子表(另一个广义表),通常会使用大写字母来表示子表,而小写字母表示原子。
递归定义:
广义表是递归定义的,即广义表的元素可以是另一个广义表。这种递归性质使得广义表能够表示复杂的数据结构。
空表和原子:
空表是一个特殊的广义表,它不包含任何元素,通常表示为 ()。
原子是广义表的基本元素,它们不是广义表,而是基本类型的数据,如数字、字符等。
长度和深度:
广义表的长度是指其包含的元素的个数。
广义表的深度是指其嵌套的层数。原子的深度为0,空表的深度为1,而包含子表的广义表的深度则大于1。
子表和表头表尾:
如果一个元素是另一个广义表,则称该元素为子表。
对于非空广义表,第一个元素称为表头,其余元素组成的表称为表尾。
以下是一些广义表语法的示例:
E = ():E 是一个空表,其长度为0。
L = (a, b):L 是一个长度为2的广义表,其元素都是原子。
A = (x, L) = (x, (a, b)):A 是一个长度为2的广义表,第一个元素是原子x,第二个元素是子表L。
C = (A, B) = ((x, (a, b)), ((x, (a, b)), y)):C 的长度为2,两个元素都是子表。
D = (a, D) = (a, (a, (a, (…)))):D 是一个递归的广义表,其长度为2,第一个元素是原子a,第二个元素是D自身。
这些示例展示了广义表的基本语法和表示方法,通过不同的组合和嵌套,可以构建出复杂的数据结构。需要注意的是,具体的语法规则可能会因不同的编程语言或应用领域而有所差异,因此在实际应用中需要参考相应的文档或规范。
广义表(Lists,又称列表)具有多种特性,以下是一些主要的特性:
- 层次性:广义表的元素可以是子表,而子表的元素还可以是子表,由此,广义表是一个多层次的结构。这种层次性使得广义表能够表示更为复杂的数据结构,满足了实际应用中对于嵌套数据的需求。
- 共享性:广义表可以为其他广义表所共享。这种共享性提高了数据的复用性,减少了数据的冗余,从而优化了存储空间的使用。
- 递归性:广义表可以是其自身的一个子表,形成递归表。递归表在定义上可以是无穷的,但在实际应用中,其长度通常是有限的。这种递归性使得广义表能够表达更为复杂和灵活的数据结构。
- 元素多样性:广义表中的元素可以是原子(如数字、字符等),也可以是另一个广义表。这种元素的多样性使得广义表能够表示更为丰富和复杂的数据关系。
- 有序性:广义表中的元素是按照一定的顺序排列的,这种有序性使得我们可以按照特定的顺序来访问和处理元素。
广义表的深度和长度:
广义表的深度定义为子表的最大嵌套层数。具体地说,空表的深度为1,原子(即不含任何括号的元素)的深度为0。而当一个元素是另一个广义表时,其深度为子表的深度加1。
广义表的长度则是指该广义表中元素的个数,包括子表。子表被算作单一元素,而不是被拆分成多个元素计算。
例如,对于广义表LS=((),a,b,(a,b,c),(a,(a,b),c)),其长度为5(因为包含5个元素:一个空子表、a、b、一个包含三个元素的子表、和一个包含两个元素的子表),深度为3(因为子表(a,(a,b),c)是嵌套层数最深的子表)。
为了更深刻的理解我们的深度和长度的概念,下面,我们来做点练习:
以下是一些广义表的例子,以及它们的长度和深度的判断:
例1: 广义表 L1 = (a, (b, c, d), e)
- 长度: 广义表 L1 包含三个元素:a,一个子表 (b, c, d),以及 e。因此,L1 的长度为 3。
- 深度: 子表 (b, c, d) 的深度为 1(因为它没有嵌套子表)。整个广义表 L1 的深度也是 1,因为最深的嵌套层数就是 1。
例2: 广义表 L2 = (f, (g, (h, i), j))
- 长度: 广义表 L2 包含两个元素:f 和一个子表 (g, (h, i), j)。因此,L2 的长度为 2。
- 深度: 子表 (g, (h, i), j) 包含另一个子表 (h, i),而 (h, i) 没有再嵌套子表。所以 (g, (h, i), j) 的深度为 2。整个广义表 L2 的深度也是 2。
例3: 广义表 L3 = ()
- 长度: 空广义表不包含任何元素,所以其长度为 0。
- 深度: 空广义表被视为一个单独的实体,因此其深度为 1。
例4: 广义表 L4 = (((a), (b, (c, d)), e)
- 长度: 广义表 L4 包含两个元素:一个子表 (((a), (b, (c, d))) 和 e。因此,L4 的长度为 2。
- 深度: 子表 (b, (c, d)) 的深度为 2,因为它包含一个嵌套子表 (c, d)。而整个广义表 L4 的最深嵌套层数也是 2(由子表 (b, (c, d)) 贡献),因此其深度为 2。
通过这些例子,我们可以看到,要确定广义表的长度,我们只需计算最外层包含的元素的个数;而要确定深度,我们需要找到最深的嵌套子表,并计算其嵌套层数。注意,在计算深度时,我们可能需要递归地检查子表的结构。
广义表的存储结构:
广义表采用链式存储结构时,通常使用链表来表示广义表的元素之间的关系。由于广义表中的元素既可以是原子也可以是子表,因此链式存储结构需要能够灵活地处理这两种情况。
在链式存储结构中,每个节点通常包含两部分信息:数据域和指针域。数据域用于存储节点的值,而指针域则用于指向其他节点。
对于广义表的链式存储,可以采用如下方式:
- 原子节点:
- 数据域:存储原子的值。
- 指针域:通常不需要额外的指针,或者可以设置一个指向下一个同层节点的指针。
- 表节点(即子表的节点):
- 数据域:通常不存储具体的值,而是作为子表的标识。
- 指针域:需要至少两个指针。一个指针指向子表的第一个元素(即表头),另一个指针指向子表的剩余部分(即表尾)。如果子表还有更深层次的子表,那么表尾指针可能需要指向下一层的第一个节点。
此外,为了区分原子节点和表节点,可以在节点结构中增加一个标志位。例如,标志位为0表示该节点是原子节点,标志位为1表示该节点是表节点。
这种链式存储结构能够灵活地表示广义表的结构,允许表中有表,且表的长度可以是任意的。同时,由于链表本身的动态性,这种存储结构也能够方便地实现广义表的插入、删除等操作。
需要注意的是,由于链式存储结构需要额外的空间来存储指针,因此在某些情况下可能会比顺序存储结构占用更多的空间。此外,链式存储结构在访问元素时通常需要从头节点开始遍历,因此在某些情况下可能不如顺序存储结构高效。
结构:
广义表节点(data数据域,child子表地址域,next后继节点地址域)
将原子节点的child域值设置为null,因此,child域是否为null成为区分原子和子表的标志。
广义表的抽象数据类型:
声明GenLList<T>接口:
public interface GenLList<T> {
boolean isEmpty();
int size();
int depth();
GenNode<T> get(int i);
GenNode<T> insert(int i,T x);
GenNode<T> insert(int i,GenList<T>genlist);
GenNode<T> remove(int i);
void clear();
GenNode<T> search(T key);
GenNode<T> remove(T key);
}
解释:
public interface GenLList<T> {
// 检查广义表是否不包含任何元素(即是否为空)。
boolean isEmpty();
// 返回广义表中包含的元素个数。
int size();
// 返回广义表的最大括号嵌套层数,即深度。
int depth();
// 返回广义表中指定位置的节点。如果索引超出范围,可能抛出异常或返回特定值。
GenNode<T> get(int i);
// 在广义表的指定位置插入一个原子元素。如果索引超出范围,可能抛出异常。
GenNode<T> insert(int i, T x);
// 在广义表的指定位置插入一个子表。如果索引超出范围,可能抛出异常。
GenNode<T> insert(int i, GenList<T> genlist);
// 从广义表中删除指定位置的节点,并返回被删除的节点。如果索引超出范围,可能抛出异常或返回null。
GenNode<T> remove(int i);
// 清空广义表,移除所有节点。
void clear();
// 在广义表中搜索具有指定值的节点,并返回第一个匹配的节点。如果没有找到,可能返回null。
GenNode<T> search(T key);
// 从广义表中删除第一个具有指定值的节点,并返回被删除的节点。如果没有找到,可能返回null。
GenNode<T> remove(T key);
}
声明广义表节点类:
public class GenNode<T> {
public T data;
public GenList<T> child;
public GenNode<T> next;
public GenNode(T data, GenList<T> child, GenNode<T> next) {
super();
this.data = data;
this.child = child;
this.next = next;
}
public GenNode()
{}
未完待续......
标签:元素,学习,GenNode,广义,深度,子表,节点 From: https://blog.csdn.net/2301_77523055/article/details/137432059