当前位置 博文首页 > kbtx的博客:利用前序遍历和中序遍历重构二叉树

    kbtx的博客:利用前序遍历和中序遍历重构二叉树

    作者:[db:作者] 时间:2021-07-09 09:51

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

    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);
    	}
    
    }
    
    cs