数据结构day3——树

1.一些概念

树:由一个或多个结点组成的有限集;有一个根结点和多棵子树。将最左边的子树的根结点称为树的最左子结点。

出度:结点的出度是指一个结点的子结点的数目。

森林:零棵或多棵树的集合

边定理:结点为n的树必定有n-1条边,因为除了根结点其余结点都有一条边连接其父亲结点。

树的周游:a.前序周游:递归的方式,先访问根结点,再访问其最左子结点所在子树,再访问其最左子结点的右兄弟结点……,后序周游则与前序周游顺序相反,最后才访问根结点的值;注意中序周游略有不同,因为每个结点可能有不止两棵子树,具体顺序视情况而定。

上述树的周游方式要求树的结点类必须给出最左子结点和右兄弟结点的访问方式:

//树结点的接口
public interface GTNode {
	public Object value();
	public boolean isLeaf();
	public GTNode parent();//返回父结点
	public GTNode leftmost_child();//返回最左子结点
	public GTNode right_sibling();//返回右边的兄弟结点
	public void setValue(Object val);
	public void setParent(GTNode par);
	public void insert_first(GTNode n);//添加新的最左子结点
	public void insert_next(GTNode n);//添加一个新的右兄弟结点
	public void remove_first();
	public void remove_next();
}
//树的接口
public interface GenTree {
	public void clear();
	public GTNode root();
	public void newroot(Object value,GTNode first,GTNode sib);
}

2.父指针表示法

即,仅保存到父结点的指针;那么对于上述最左子结点和右兄弟结点的操作就不能实现了;但父指针表示法也有其用处;

父指针表示法可以用数组存储,每个结点存储其父亲结点所在的位置序号。

1)UNION/FIND算法

FIND:查找一个给定结点的根结点的过程;

UNION:判断两个结点是否在一个集合中(相同祖先),如果不是,则将两个集合归并为一个集合。

下面是使用父指针表示法实现的树:

//用于UNION/FIND算法的树的结点类
public class GTNode_UF {
	private GTNode_UF par;
	public GTNode_UF(){
		par=null;
	}
	public GTNode_UF parent(){
		return par;
	}
	public GTNode_UF setParent(GTNode_UF newpar){
		return par=newpar;
	}
}
//使用UNION/FIND的树的类
public class GenTree_UF {
	private GTNode_UF[] array;
	public GenTree_UF(int size){
		array=new GTNode_UF[size];
		for(int i=0;i<size;i++){
			array[i]=new GTNode_UF();
		}
	}
	//检查两个结点是否不在同一个集合
	public boolean differ(int a,int b){
		GTNode_UF root1=FIND(array[a]);
		GTNode_UF root2=FIND(array[b]);
		return root1!=root2;
	}
	
	//FIND函数用于找到结点的根结点
	private GTNode_UF FIND(GTNode_UF curr){
		while(curr.parent()!=null)
			curr=curr.parent();
		return curr;
	}
	
	private void UNION(int a,int b){
		GTNode_UF root1=FIND(array[a]);
		GTNode_UF root2=FIND(array[b]);
		if(root1!=root2){
			//二者不在同一棵树,则进行归并
			root2.setParent(root1);
		}
	}
}

UNION/FIND算法在等价类划分应用中十分有用。FIND的效率取决于结点到其根结点的距离,因此在UNION时应该将结点树少的树作为子树,尽量降低树的高度,这种方式叫做重量权衡合并规则。

另一种降低树高办法是路径压缩,即将所有结点都设置为直接指向其根结点。

3.子结点表表示法

这种办法是为了使得结点可以访问其所有子结点,需要保存一个指针,指向由所有子结点所构成的链表。

这种方式对于需要访问兄弟的情况来说效率非常低,因为需要先访问父亲结点,再去访问子结点的链表,找到自己,自己的下一个才是自己的右兄弟。而且每个结点的存储空间大小变成不固定的了,因为每个结点的子结点数目不同。

4.左子结点/右兄弟结点表示法

每个结点存储到父亲,左儿子,右兄弟的指针(在数组中的位置序号),这样的效率更高,每个结点的存储空间也是固定的。

这种方法中,在数的归并时,需要调整三个指针:设r1需要指向r2,r2的最左子结点是x,则要调整的是,r2的右兄弟结点指向x,父亲结点指向r1;r1的最左子结点指向r2;

5.动态结点表示法

1)为每个结点分配可变的存储空间,本质与子结点表示法相同。

2)提前知道子结点数目时,为每个结点留固定的子结点空间,保留一个域存放子结点数目。

6.动态“左子结点/右兄弟结点表示法”

本质是用二叉树来表示森林,由森林转化而来的二叉树中,左子结点是原来的最左子结点,右子结点是原来的右侧兄弟结点。

7.K叉树,最多K个子结点的树,也有满k叉树和完全k叉树,特性与二叉树类似。

8.树的顺序表示法

将树的结点按照树的先序周游的顺序存储在数组中。以二叉树为例,为了区分叶子结点和分支结点,可以假设每个结点都有两棵子树,都占用存储空间;那么这样只要是两棵子树都为空的结点就是叶结点了。

然而,上述的表示法,空指针占据的空间太大了,按照满二叉树定理的推广,非空二叉树中,空子树的数目等于结点数目+1,所以有一半的空间浪费了。

紧凑的顺序表示法:如果一个结点的两个子树都为空,则说明此结点是叶结点,则不必保存其两棵空子树。但是需要标记分支结点用以区别,而叶结点不必加标记。这样的话每个结点都会增加一位来表示自己是叶结点还是分支结点,但增加一位比增加一个空指针来说节省了相当多的空间。

子结点表结束标志法:用)来表示子结点表的结束。这样所有叶结点后都会跟着一个),因为叶结点没有子结点了,已经结束了。如果叶结点是其所在子树的最后一个子结点,其后将多跟一个)。 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值