利用前序遍历和中序遍历重构二叉树

我们考虑一种简单的情况,现在假定有这样一颗二叉树:
在这里插入图片描述
显然其前序遍历和中序遍历分别为

int[] preOrder = {7,6,4,3,5,2,1};
int[] midOrder = {4,6,3,7,2,5,1};

我们可以再根据前序遍历和中序遍历还原这个二叉树,其原理为:
前序遍历总是按照根节点-左子树-右子树的顺序遍历,中序遍历总是按照左子树-根节点-右子树的顺序遍历,因此在初始状态下,preOrder[0]中存储的就是整棵树的根节点的值。
我们设计一个pos函数用于找到数字a在指定数组array[]中的位置:

public static int pos(int a, int[] array) {
	for(int i=0;i<array.length;i++) {
		//System.out.println(a + " Searching...");
		if(array[i]==a) return i;
	}
	System.out.println(a + " not found!");
	return -1;
}

显然,对于中序遍历来说,位于 pos(preOrder[0],midOrder) 左侧的都是这个根节点的左子树,位于 pos(preOrder[0],midOrder) 右侧的都是这个根节点的右子树。

我们令 root_pos_at_midOrder = pos(preOrder[0],midOrder)

假定我们在搜索整棵树的根节点时,指定的搜索范围为

数组名起始位置终止位置
preOrder0preOrder.length-1
midOrder0midOrder.length-1

在这里插入图片描述
现在让我们把目光转向根节点的子树:
在左子树中,我们同样要从preOrder和midOrder数组获取这个左子树自身的根节点、左子树和右子树信息,获取方法与前面类似,但我们的搜寻范围有所变化:

那么当搜索它的左子树时,范围应该为:

数组名起始位置终止位置
preOrder1pos(midOrder[root_pos_at_midOrder-1],preOrder)
midOrder0root_pos_at_midOrder - 1

在这里插入图片描述
类似地,搜索它的右子树时,范围应该为:

数组名起始位置终止位置
preOrderpos(midOrder[root_pos_at_midOrder-1],preOrder) + 1preOrder.length-1
midOrderroot_pos_at_midOrder + 1midOrder.length-1

在这里插入图片描述
显然,我们可以定义一个reBuild方法,指定当前的根节点和两个数组的起止位置,递归调用就能将二叉树重构出来

package com.ds.binTree;

class TreeNode{
	private StringBuilder sb = new StringBuilder();
	public int value;
	public TreeNode left,right;
	private String toString(String prefix) {
		sb.append(prefix).append(value).append('\n');
		if(left!=null)
			sb.append(left.toString(prefix + "-L"));
		if(right!=null)
			sb.append(right.toString(prefix + "-R"));
		return sb.toString();
	}
	public String toString() {
		return toString("");
	}
}

public class RebuildBinTree {
	
	static int[] preOrder = {7,6,4,3,5,2,1};
	static int[] midOrder = {4,6,3,7,2,5,1};
	/**
	 * 找到数字a在数组array中的位置
	 * @param a 数字
	 * @param array 数组
	 * @return
	 */
	public static int pos(int a, int[] array) {
		for(int i=0;i<array.length;i++) {
			//System.out.println(a + " Searching...");
			if(array[i]==a) return i;
		}
		System.out.println(a + " not found!");
		return -1;
	}
	/**
	 * 重建二叉树
	 * @param root 根节点
	 * @param preOst 先序遍历数组的起始位置
	 * @param preOed 先序遍历数组的终止位置
	 * @param midOst 中序遍历数组的起始位置
	 * @param midOed 中序遍历数组的终止位置
	 */
	public static void reBuild(TreeNode root,int preOst,int preOed,int midOst,int midOed) {
		//先序遍历的起始位置就是根节点
		root.value = preOrder[preOst];
		//找出根节点在中序遍历数组中的下标,下标小于它的就是它的左子树,大于它的是它的右子树
		int root_pos_at_midOrder = pos(root.value,midOrder);
		
		//表示左子树存在
		if(root_pos_at_midOrder - midOst > 0) {
			//为左子树分配空间
			root.left = new TreeNode();
			//为左子树指出范围,递归创建子树
			reBuild(root.left, preOst + 1, pos(midOrder[root_pos_at_midOrder-1],preOrder), midOst, root_pos_at_midOrder - 1);
		}
		//表示右子树存在
		if(midOed - root_pos_at_midOrder > 0) {
			//创建右子树
			root.right = new TreeNode();
			//为右子树指出范围,递归创建子树
			reBuild(root.right, pos(midOrder[root_pos_at_midOrder-1],preOrder) + 1,  preOed, root_pos_at_midOrder + 1, midOed);
		}
	}
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		TreeNode root = new TreeNode();
		reBuild(root, 0, preOrder.length-1, 0, midOrder.length-1);
		System.out.print(root);
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值