剑指offer——4. 重建二叉树分解

关于树的前序遍历、中序遍历、后序遍历:

https://www.jianshu.com/p/1c50b23fcc30

总结:

1) 前、中、后是相对于根节点的

2)当知道前序+中序序列,可以推出树结构,

      当知道中序+后序序列,可以推出树结构,

      当知道前序+后序序列,无法推出树结构。

 

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

链接:https://www.nowcoder.com/questionTerminal/8a19cbe657394eeaac2f6ea9b0f6fcf6?answerType=1&f=discussion
来源:牛客网
 

前序+中序分解过程图示如下(王道数据结构P120)

 

思路:

  1. 由先序序列第一个pre[0]在中序序列中找到根节点位置gen
  2. gen为中心遍历
    • 0~gen左子树
      • 子中序序列:0~gen-1,放入vin_left[]
      • 子先序序列:1~gen放入pre_left[]+1可以看图,因为头部有根节点
    • gen+1~vinlen为右子树
      • 子中序序列:gen+1 ~ vinlen-1放入vin_right[]
      • 子先序序列:gen+1 ~ vinlen-1放入pre_right[]
  3. 由先序序列pre[0]创建根节点
  4. 连接左子树,按照左子树子序列递归(pre_left[]vin_left[]
  5. 连接右子树,按照右子树子序列递归(pre_right[]vin_right[]
  6. 返回根节点

第一个版本的代码:O(n^2)

没接触过树结构这块,看了一下上面的关于树的前序、中序、后续遍历的资料,自己看着序列画了几棵树,感觉有点思路。

大概能总结出来是要先在前序序列中找顶点,接着在中序序列中找到对应index,可以作为划分顶点左右子树的结点,将左右分成两部分继续递归,修改了几次,自己测试是没问题的。

但是写出的方法是O(n^2)的,主要原因在于没有发现前序序列的截取规律,这里想到了前序序列可能能够截取,但是没有想通截取的长度应该是多少。于是第一个版本是双重循环的,没想到提交上去也能过哈哈哈哈,看来测试用例的序列没有太长。

    import java.util.Arrays;
	
    public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
		if(in.length == 0){
			return null;
		}
		int preIndex = 0;//前序遍历index
		int inIndex = 0;//中序遍历index
		boolean falg = false;
		for (int i = 0; i < pre.length; i++) {
			for (int j = 0; j < in.length; j++) {
                                //在pre中找到第一个能与in序列匹配上的元素
				if(pre[i]==in[j]){
					preIndex = i;
					inIndex = j;
					System.out.println("当前顶点:"+pre[preIndex]+",in总长度:"+in.length+",index位置:"+inIndex);
					falg = true;
					break;
				}
			}
			if(falg){
				break;
			}
		}
                //当前结点
		TreeNode tempNode = new TreeNode(pre[preIndex]);		
		tempNode.left = reConstructBinaryTree(pre, Arrays.copyOfRange(in, 0, inIndex));
		tempNode.right = reConstructBinaryTree(pre,Arrays.copyOfRange(in, inIndex+1, in.length));
		return tempNode;
    }

修改后的代码:O(n)

测试过了以后看了一下解题思路,发现pre也是可以根据inIndex截取的,对比上面的图想了一下:A结点为根节点,

在中序序列中,左子树——A——右子树,

在前序序列时,顶点A——左子树——右子树,

那么pre中,A后面跟着的左子树的长度必然与中序中A左边的长度是相同的。知道了A在中序序列中的位置inIndex,则可以根据此索引来截取pre了。

修改后的代码也简洁了很多:

public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
		if(pre.length == 0 || in.length == 0){
			return null;
		}
		int inIndex = 0;
		TreeNode tempNode = new TreeNode(pre[0]);//每次只要找pre中第一位即可	
		for (int i = 0; i < in.length; i++) {
				if(pre[0]==in[i]){
					inIndex = i;
					tempNode.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, inIndex + 1), Arrays.copyOfRange(in, 0, inIndex));
					tempNode.right = reConstructBinaryTree(Arrays.copyOfRange(pre, inIndex + 1, pre.length),Arrays.copyOfRange(in, inIndex+1, in.length));
					break;
				}
		}
		return tempNode;
    }

另外就是还要注意Arrays.copyOfRange的用法,在截取时,左闭右开,所以左边要从需要的那一位开始截取。在截取in时,曾误将左边的界限:inIndex+1写为inIndex,结果造成了死循环。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值