广义表的学习

本文详细介绍了广义表作为一种数据结构,它是线性表的推广,允许元素嵌套形成层次结构。文章讨论了广义表的递归结构、可变长度、不唯一性和灵活性,并重点阐述了链式存储结构的应用,以及如何通过抽象数据类型实现广义表的操作,如插入、删除和搜索等。
摘要由CSDN通过智能技术生成

广义表的定义:

广义表(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. 层次性:广义表的元素可以是子表,而子表的元素还可以是子表,由此,广义表是一个多层次的结构。这种层次性使得广义表能够表示更为复杂的数据结构,满足了实际应用中对于嵌套数据的需求。
  2. 共享性:广义表可以为其他广义表所共享。这种共享性提高了数据的复用性,减少了数据的冗余,从而优化了存储空间的使用。
  3. 递归性:广义表可以是其自身的一个子表,形成递归表。递归表在定义上可以是无穷的,但在实际应用中,其长度通常是有限的。这种递归性使得广义表能够表达更为复杂和灵活的数据结构。
  4. 元素多样性:广义表中的元素可以是原子(如数字、字符等),也可以是另一个广义表。这种元素的多样性使得广义表能够表示更为丰富和复杂的数据关系。
  5. 有序性:广义表中的元素是按照一定的顺序排列的,这种有序性使得我们可以按照特定的顺序来访问和处理元素。

广义表的深度和长度:

广义表的深度定义为子表的最大嵌套层数。具体地说,空表的深度为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。

通过这些例子,我们可以看到,要确定广义表的长度,我们只需计算最外层包含的元素的个数;而要确定深度,我们需要找到最深的嵌套子表,并计算其嵌套层数。注意,在计算深度时,我们可能需要递归地检查子表的结构。

广义表的存储结构:

广义表采用链式存储结构时,通常使用链表来表示广义表的元素之间的关系。由于广义表中的元素既可以是原子也可以是子表,因此链式存储结构需要能够灵活地处理这两种情况。

在链式存储结构中,每个节点通常包含两部分信息:数据域和指针域数据域用于存储节点的值,而指针域则用于指向其他节点。

对于广义表的链式存储,可以采用如下方式:

  1. 原子节点
    • 数据域:存储原子的值。
    • 指针域:通常不需要额外的指针,或者可以设置一个指向下一个同层节点的指针。
  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()
	{}

声明广义表类:

public class GenList<T> implements GenLList<T> {
	public GenNode<T> head;// 头指针,指向头结点

	public GenList()// 构造空广义表,表名为null,就是没有表名;
	{
		this.head = new GenNode<T>(null, null, null);// 创建头结点
	}

	public GenList(T data)// 构造空广义表,表名为data;
	{
		this.head = new GenNode<T>(data, null, null);// 头结点的data域存储表名
	}

	// 构造广义表,data指定表名,atoms[]指定原子初值,构造的是线性表,算法同单链表
	// 节点次序与数组元素次序相同,采用尾差入构造
	public GenList(T data, T[] atoms) {
		this(data);// 创建空广义表,只有头结点
		GenNode<T> rear = this.head;
		for (int i = 0; i < atoms.length; i++) {
			rear.next = new GenNode<T>(atoms[i], null, null);// 尾差入
			rear = rear.next;
		}
	}

	public boolean isEmpty() {
		return this.head.child == null && this.head.next == null;

	}

	public T getName()// 返回表名
	{
		return this.head.data;
	}

	// 返回第i个元素节点,0<=i<表长度,若i越界,返回null,O(n)//算法同单链表
	public GenNode<T> get(int i) {
		GenNode<T> p = this.head.next;
		for (int j = 0; p != null && j < i; j++)// 遍历,寻找第i个节点(p)指向
			p = p.next;
		return (i >= 0 && p != null) ? p : null;// 返回第i个节点
	}

	public String toString()// 返回this广义表的描述字符串
	{
		return this.toString(this);
	}

	public String toString(GenList<T> genlist) {
		String str = (genlist.head.data == null ? "" : genlist.head.data.toString()) + "(";
		for (GenNode<T> p = genlist.head.next; p != null; p = p.next) {
			str += toString(p) + (p.next != null ? "," : "");
		}
		return str + ")";
	}

	public String toString(GenNode<T> p) {
		if (p == null)
			return null;
		return p.child == null ? p.data.toString() : toString(p.child);
	}
	public int size()//返回广义表长度,算法同单链表
	{
		int i=0;
		for(GenNode<T> p=this.head.next;p!=null;p=p.next)
			i++;
		return i;
	}
	public int depth(){//返回广义表深度,递归方法
		int max=1;
		for(GenNode<T>p=this.head.next;p!=null;p=p.next)
		{
			if(p.child!=null)
			{
				int d=p.child.depth();//递归调用,返回子表深度
				if(max<=d)//记住最大子表深度
					max=d+1;//当前广义表深度为子表深度加1;
			}
			
		}
		return max;
	}
	//插入原子x作为第i个元素,x!=null,返回插入的原子节点
	//对i容错,若i<0,头插入,若i>
	public GenNode<T> insert(int i,T x)
	{
		if(x==null)
			return null;
		GenNode<T> front=this.head;//fornt指向头结点
		for(int j=0;front.next!=null&&j<i;j++)//寻找第i-1个或最后一个节点(front指向)
		{
			front=front.next;			
		}
		front.next=new GenNode<T>(x,null,front.next);//z在front 之后插入值为x节点
		return front.next;//返回插入的原子节点
	}
	public GenNode<T> insert(T x)
	{
		return insert(Integer.MAX_VALUE,x);
	}
	public GenNode<T> insert(int i,GenList<T>genlist)
	{
		if(genlist==null||this==genlist)//不插入节点
			return null;
		GenNode<T>front=this.head;//fornt指向头结点
		for(int j=0;front.next!=null&&j<i;j++)//寻找第i-1个或最后一个节点(front指向)
			front =front.next;
		front.next=new GenNode<T>(genlist.head.data,genlist,front.next);
		return front.next;
	}
	public GenNode<T> insert(GenList<T>genlist)
	{
		return insert(Integer.MAX_VALUE,genlist);
	}
}

测试类:

public class TestGenList {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String[] atoms={"a","b"};
		GenList<String>L=new GenList<String>("L",atoms);
		GenList<String>T=new GenList<String>("T");
		T.insert("c");
		T.insert(L);
		GenList<String> G=new GenList<String>("G");
		G.insert("f");
		G.insert(L);
		G.insert(T);
		System.out.println(G.toString());
	}

}

  • 34
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值