数据结构之——树(二)

二叉树的二叉链表实现

对二叉树的操作由二叉树结点类二叉树类共同。采用二叉链表的二叉树结点类和二叉树类设计如下。

1.二叉链表结点类

声明二叉树的二叉链表结点类BinaryNode< T >如下,T指定结点的元素类型。

package Tree;

public class BinaryNode<T> {		//二叉树的二叉链表结点类,T指定结点的元素类型
	public T data;		//数据域,存储数据元素
	public BinaryNode<T> left,right;		//地址域,分别指向左、右孩子结点
	//构造结点,data指定元素,left、right分别指向左孩子和右孩子结点
	public BinaryNode(T data,BinaryNode<T> left,BinaryNode<T> right){
		this.data=data;
		this.left=left;
		this.right=right;
		}
	public BinaryNode(T data) {			//构造元素为data的叶子结点
		this(data,null,null);
	}
	public String toString() {			//返回结点数据域的描述字符串
		return this.data.toString();
		}
	public boolean isLeaf() {			//判断是否叶子结点
		return this.left==null&&this.right==null;
		
	}
	

}

2.采用二叉链表存储的二叉树类声明

声明二叉树类BinaryTree< T >如下,采用二叉链表存储,其中成员变量root指向二叉树的根结点。

package Tree;

public class BinaryTree<T> {
	public BinaryNode<T> root;			//根结点,二叉链表结点结构
	public BinaryTree() {				//构造空二叉树
		
	}
	public boolean isEmpty() {			//判断是否空二叉树
		return this.root==null;
	}
	......								//稍后给出其他成员方法的声明和实现

}

3.二叉树插入结点

在二叉树的二叉链表中插入一个结点,需要修改该结点的父母结点的left域或right域。因此,插入函数要指定插入结点作为哪个结点的左孩子还是右孩子。

我们在BinaryTree< T >类声明以下重载的成员方法,插入结点。

public BinaryNode insert(T x){ //插入x作为根结点,原根结点作为x的左孩子;返回插入结点

	public BinaryNode<T> insert(T x){	//插入x作为根结点,原根结点作为x的左孩子;返回插入结点
		return this.root=new BinaryNode<T>(x,this.root,null);
	}
	//插入x为parent结点的左/右孩子,leftChild指定孩子,取值为true(左)、false(右)
	//parent的原左/右孩子成为x结点的左/右孩子;返回插入结点
	//若x==null,不插入,返回null。若parent==null,java抛出空对象异常
	public BinaryNode<T> insert(BinaryNode<T> parent,T x,boolean leftChild){
		if(x==null) {
			return null;
		}
		if(leftChild) {			//插入x为parent结点的左/右孩子,返回插入结点
			return parent.left=new BinaryNode<T>(x,parent.left,null);	
		}
		return parent.right=new BinaryNode<T>(x,null,parent.right);
	}

4.二叉树删除子树

在二叉树中删除一个结点,不仅要修改其父母结点的left或者right域,还要约定如何调整子树结构的规则,即删除一个结点,原先以该结点为根的子树则变成由原左子树和右子树组成的森林,约定一种规则使这个森林组成一课子树。此处,因为无法约定左右子树的合并规则,只能删除以一个结点为根的一棵子树。

BinaryTree< T >类声明以下成员方法,删除子树,Java自动收回被删除子树占用的存储空间。

//删除parent结点的左或者右子树,leftChild指定子树,取值为true(左)、false(右)
	public void remove(BinaryNode<T> parent,boolean leftChild) {
		if(leftChild) {
			parent.left=null;		//若parent==null,Java抛出空对象异常
		}else {
			parent.right=null;
		}
	}
	public void clear() {			//删除二叉树的所有结点
		this.root=null;
		
	}

5.二叉树孩子优先遍历算法(深度优先遍历DFS)

1.前序遍历二叉树算法描述

由于先根次序遍历规则是递归的,采用递归算法实现的递归方法必须要有参数,通过不同的实际参数区别递归调用执行中的多个方法。而二叉树类必须提供从根结点开始遍历的成员方法,因此,每种递归的遍历算法由两个重载的成员方法实现。

BinaryTree< T >类声明以下重载的成员方法,以先根次序遍历二叉树,采用递归算法实现递归的先根次序遍历规则。

public void preorder() {		//输出先根次序遍历序列
		preorder(this.root);		//先根次序遍历以root结点为根的二叉树
		System.out.println();
		
		
	}
	private void preorder(BinaryNode<T> p) {		//先根次序遍历以root结点为根的二叉树
		if(p!=null) {
			System.out.println(p.data.toString()+"");	//先访问当前结点元素
			preorder(p.left);			//按先根次序遍历p的左子树,递归调用,参数为左孩子
			preorder(p.right);			//按先根次序遍历p的右子树,递归调用,参数为右孩子
		}
		
	}

一棵二叉树由多棵子树组成,一个结点也是一棵子树的根。二叉树基于遍历的递归方法,必须以某个结点p为参数,表示遍历以p结点为根的子树。preorder(p)算法说明如下。

(1)p从root根结点开始执行,表示遍历一棵二叉树。

(2)当p指向某个结点时,按先根次序访问结点元素后,再分别遍历其左子树、右子树。由于遍历子树的规则相同,只是子树的根结点不同,所以,递归调用当前preorder()方法,参数分别是p结点的左孩子p.left和右孩子p.right。

(3)当p为空子树时,当前递归方法执行结束,返回调用方法。

2.先根、中根、后跟次序遍历二叉树

BinaryTree< T >类声明以下重载的成员方法,分别以先根、中根、后跟三种次序遍历二叉树,都是递归算法,三者之间的区别只是在于访问结点的时机不同。

public void preorder() {		//输出先根次序遍历序列
		preorder(this.root);		//先根次序遍历以root结点为根的二叉树
		System.out.println();
		
		
	}
	private void preorder(BinaryNode<T> p) {		//先根次序遍历以root结点为根的二叉树
		if(p!=null) {
			System.out.println(p.data.toString()+"");	//先访问当前结点元素
			preorder(p.left);			//按先根次序遍历p的左子树,递归调用,参数为左孩子
			preorder(p.right);			//按先根次序遍历p的右子树,递归调用,参数为右孩子
		}
		
	}
	public String toString() {			//返回先根次序遍历二叉树所有结点的描述字符串,包括空子树标记
		return toString(this.root);
	}
	private String toString(BinaryNode<T> p) {			//返回先根次序遍历以p为根的子树描述串,递归算法
		if(p==null) {
			return"^";			//输出空子树标记
		}
		return p.data.toString()+""+toString(p.left)+toString(p.right);		//递归调用
		
	}
	public void inorder() {		//输出中根次序遍历序列
		inorder(this.root);
		System.out.println();
		
	}
	public void inorder(BinaryNode<T> p) {
		if(p!=null) {
			inorder(p.left);		//中根次序遍历以p结点为根的子树,递归方法
			System.out.println(p.data.toString()+"");
			inorder(p.right);		//中根次序遍历p的右子树,递归调用
		}
	}
	public void postorder() {			//输出后跟次序遍历序列
		postorder(this.root);
		System.out.println();
		
	}
	public void postorder(BinaryNode<T> p) {		//后跟次序遍历以p结点为根的子树,递归方法
		if(p!=null) {
			postorder(p.left);
			postorder(p.right);
			System.out.println(p.data.toString()+"");		//后访问当前结点元素
		}
		
	}

6.构造二叉树

图示法能够直观描述二叉树的逻辑结构,但不便于作为计算机输入的表达方式。

由二叉树的特性可知,构造一棵二叉树必须明确以下两种关系:

1.结点与其父母结点以及孩子结点之间的层次关系。

2.兄弟结点左或右的次序关系。

以下讨论三种能够唯一确定一棵二叉树的表示法。

(1)由二叉树的一种遍历序列不能唯一确定一棵二叉树

已知一棵二叉树,可唯一确定其先根、中根、后跟和层次遍历序列;反之,已知二叉树的一种遍历序列却不能唯一确定一棵二叉树。

例如,已知一棵二叉树的先根遍历序列是AB,则能够确定A是根结点,并且B是A的孩子结点,但不能确定是哪个孩子,可能是左孩子,也可能是右孩子。因此,这会得到两种结果。这是因为先根遍历序列只反映父母与孩子结点之间的层次关系,没有反映兄弟结点间的左右次序。

(2)先根和中根序列表示

由于先根次序或后根次序反映父母与孩子结点从层次关系,中根次序反映兄弟结点间的左右次序。所以我们已知二叉树的先根和中根两种次序的遍历序列,可唯一确定一棵二叉树。

下面我们来证明一下,设数组prelist和inlist分别表示一棵二叉树的先根和中根次序遍历序列,两序列长度均为n。

1.由先根遍历次序可知,该二叉树根为prelist[0];该根结点必定在中根次序遍历序列中,设根结点在中根序列inlist中的位置为i(-1<i<n),即有inlist[i]=prelist[0]。

2.由中根遍历次序可知,inlist[i]之前的结点在根的左子树上,后面的结点在根的右子树上。因此,根的左子树由i个结点组成。以此递归,keey1唯一确定一棵二叉树。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值