算法题:剑指 Offer 07. 重建二叉树(题目+思路+代码+注释)时空时间优先选O(N) O(N) 2ms击败97%、76%用户,空间优先则选O(N*logN) O(1)
题目
剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例 1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
示例 2:
Input: preorder = [-1], inorder = [-1]
Output: [-1]
限制:
0 <= 节点个数 <= 5000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
题目中的null你可能不太理解,那是打印的算法上,与直接理解打印数不一样,他打印了非叶子节点的左右节点,空的就是null,这个不在乎这么多,我们要的是重建这颗二叉树并且返回根节点对象就可以了
第一步观察数据,根据二叉树遍历特性找规律,逆向解出来。
preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
首先第一点,在前序遍历的时候第一个数字一定是根节点,那么我们拿到了根节点,如何去获取左右的子节点呢?
根据中序遍历的特性,根节点在最中间,因此我们拿着从前序遍历里得到的3到中序遍历里找到3的下标,于是乎在中序遍历里3的左边的数字全部属于左子树,3右边的数字全部是右子树的。也就是左边的9是左子树,15 20 7是右子树,这样我们就知道了左右子树有多少个数字了,回到前序遍历中,前序遍历是: 根节点 左子树 右子树,而左子树的第一个就是根节点的左子节点,右子树的第一个就是右子树的第一个节点,因此我们就可以找出根节点的左右节点,按照这个方法递归下去,就能把每个节点的左右节点找出来,最后整颗二叉树就建好了。我们详细步骤来解释下:
代码
提交代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder.length == 0){
return null;
}
TreeNode root = new TreeNode(preorder[0]);
HashMap<Integer,Integer> map = new HashMap<>(preorder.length);
for (int i = 0,len = preorder.length; i < len;i++){
map.put(inorder[i],i);
}
rebuild(map,root,preorder,inorder,0,0,preorder.length-1);
return root;
}
/**
* 重建二叉树函数
* @param map 中序遍历 k:数字 v:下标
* @param root 当前子树的根节点对象
* @param preorder 前序遍历数组
* @param inorder 中序遍历数组
* @param rootInPreOrderIndex 根节点在前序遍历数组中的下标
* @param left 当前root点为根节点的子树在中序遍历数组中的起始下标
* @param right 当前root点为根节点的子树在中序遍历数组中的终止下标
*/
public void rebuild(HashMap<Integer,Integer> map,TreeNode root,int[] preorder, int[] inorder,int rootInPreOrderIndex,int left,int right){
if (left < right){
int rootNum = preorder[rootInPreOrderIndex];
int rootInOrderIndex = map.get(rootNum);
int leftTreeSize = rootInOrderIndex - left;
int rightTreeSize = right - rootInOrderIndex;
/*System.out.println("当前节点"+rootNum+" root在先序遍历上的下标:"+rootInPreOrderIndex+"左右子树在中序遍历上的 left:"+left+" right:"+right+" 左子树大小:"+leftTreeSize+" 右子树大小:"+rightTreeSize);
System.out.println("整颗树");
for (int i = left; i<= right;i++){
System.out.print(preorder[i]+" ");
}
System.out.println();
System.out.println("左子树");
for (int i = 0; i< leftTreeSize;i++){
System.out.print(preorder[left+i+1]+" ");
}
System.out.println();
System.out.println("右子树");
for (int i = 0; i< rightTreeSize;i++){
System.out.print(preorder[left+leftTreeSize+i+1]+" ");
}
System.out.println();*/
//左子树
if (leftTreeSize > 0){
TreeNode leftNode = new TreeNode(preorder[rootInPreOrderIndex+1]);
root.left = leftNode;
//以左节点为根继续递归建立子树
rebuild(map,root.left,preorder,inorder,rootInPreOrderIndex+1,left,rootInOrderIndex-1);
}
//右子树
if (rightTreeSize > 0){
TreeNode rightNode = new TreeNode(preorder[rootInPreOrderIndex+leftTreeSize+1]);
root.right = rightNode;
rebuild(map,root.right,preorder,inorder,rootInPreOrderIndex+leftTreeSize+1,rootInOrderIndex+1,right);
}
}
}
}
测试代码(有提示打印)
附带对二叉树前序遍历、中序遍历、后序遍历的打印函数以供测试用
对递归中执行的每一步打印输出关键数据,便于理解,解除注释即可
package leetcode.offer.q07;
import java.util.HashMap;
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder.length == 0){
return null;
}
TreeNode root = new TreeNode(preorder[0]);
HashMap<Integer,Integer> map = new HashMap<>(preorder.length);
for (int i = 0,len = preorder.length; i < len;i++){
map.put(inorder[i],i);
}
rebuild(map,root,preorder,inorder,0,0,preorder.length-1);
return root;
}
/**
* 重建二叉树函数
* @param map 中序遍历 k:数字 v:下标
* @param root 当前子树的根节点对象
* @param preorder 前序遍历数组
* @param inorder 中序遍历数组
* @param rootInPreOrderIndex 根节点在前序遍历数组中的下标
* @param left 当前root点为根节点的子树在中序遍历数组中的起始下标
* @param right 当前root点为根节点的子树在中序遍历数组中的终止下标
*/
public void rebuild(HashMap<Integer,Integer> map,TreeNode root,int[] preorder, int[] inorder,int rootInPreOrderIndex,int left,int right){
if (left < right){
int rootNum = preorder[rootInPreOrderIndex];
int rootInOrderIndex = map.get(rootNum);
int leftTreeSize = rootInOrderIndex - left;
int rightTreeSize = right - rootInOrderIndex;
/*System.out.println("当前节点"+rootNum+" root在先序遍历上的下标:"+rootInPreOrderIndex+"左右子树在中序遍历上的 left:"+left+" right:"+right+" 左子树大小:"+leftTreeSize+" 右子树大小:"+rightTreeSize);
System.out.println("整颗树");
for (int i = left; i<= right;i++){
System.out.print(preorder[i]+" ");
}
System.out.println();
System.out.println("左子树");
for (int i = 0; i< leftTreeSize;i++){
System.out.print(preorder[left+i+1]+" ");
}
System.out.println();
System.out.println("右子树");
for (int i = 0; i< rightTreeSize;i++){
System.out.print(preorder[left+leftTreeSize+i+1]+" ");
}
System.out.println();*/
//左子树
if (leftTreeSize > 0){
TreeNode leftNode = new TreeNode(preorder[rootInPreOrderIndex+1]);
root.left = leftNode;
//以左节点为根继续递归建立子树
rebuild(map,root.left,preorder,inorder,rootInPreOrderIndex+1,left,rootInOrderIndex-1);
}
//右子树
if (rightTreeSize > 0){
TreeNode rightNode = new TreeNode(preorder[rootInPreOrderIndex+leftTreeSize+1]);
root.right = rightNode;
rebuild(map,root.right,preorder,inorder,rootInPreOrderIndex+leftTreeSize+1,rootInOrderIndex+1,right);
}
}
}
/**
* 前序遍历
* @param root
*/
public void preOrderPrintTree(TreeNode root){
if (root != null){
System.out.print(root.val+" ");
if (root.left != null){
preOrderPrintTree(root.left);
}
if (root.right != null){
preOrderPrintTree(root.right);
}
}
}
/**
* 中序遍历
* @param root
*/
public void inOrderPrintTree(TreeNode root){
if (root != null){
if (root.left != null){
inOrderPrintTree(root.left);
}
System.out.print(root.val+" ");
if (root.right != null){
inOrderPrintTree(root.right);
}
}
}
/**
* 后序遍历
* @param root
*/
public void sufOrderPrintTree(TreeNode root){
if (root != null){
if (root.left != null){
sufOrderPrintTree(root.left);
}
if (root.right != null){
sufOrderPrintTree(root.right);
}
System.out.print(root.val+" ");
}
}
public static void main(String[] args) {
Solution solution = new Solution();
//前序遍历、中序遍历、后序遍历示范
/*TreeNode root = new TreeNode(3);
root.left = new TreeNode(9);
root.right = new TreeNode(20);
TreeNode tmp = root.right;
tmp.left = new TreeNode(15);
tmp.right = new TreeNode(7);
solution.preOrderPrintTree(root);
System.out.println();
solution.inOrderPrintTree(root);
System.out.println();
solution.sufOrderPrintTree(root);*/
int[] preOrder = {1,2,3};
int[] inOrder = {3,2,1};
TreeNode root = new Solution().buildTree(preOrder,inOrder);
solution.preOrderPrintTree(root);
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
空间优先代码(去掉了哈希表,手动在子树内搜索下标)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder.length == 0){
return null;
}
TreeNode root = new TreeNode(preorder[0]);
rebuild(root,preorder,inorder,0,0,preorder.length-1);
return root;
}
public void rebuild(TreeNode root,int[] preorder, int[] inorder,int rootIndex,int left,int right){
if (left < right){
int rootNum = root.val;
int rootInOrderIndex = left;
while (rootInOrderIndex <= right){
if (inorder[rootInOrderIndex] == rootNum){
break;
}else {
rootInOrderIndex++;
}
}
int leftTreeSize = rootInOrderIndex - left;
int rightTreeSize = right - rootInOrderIndex;
/*System.out.println("当前节点"+rootNum+" root在先序遍历上的下标:"+rootInPreOrderIndex+"左右子树在中序遍历上的 left:"+left+" right:"+right+" 左子树大小:"+leftTreeSize+" 右子树大小:"+rightTreeSize);
System.out.println("整颗树");
for (int i = left; i<= right;i++){
System.out.print(preorder[i]+" ");
}
System.out.println();
System.out.println("左子树");
for (int i = 0; i< leftTreeSize;i++){
System.out.print(preorder[left+i+1]+" ");
}
System.out.println();
System.out.println("右子树");
for (int i = 0; i< rightTreeSize;i++){
System.out.print(preorder[left+leftTreeSize+i+1]+" ");
}
System.out.println();*/
//左子树
if (leftTreeSize > 0){
TreeNode leftNode = new TreeNode(preorder[rootIndex+1]);
root.left = leftNode;
//以左节点为根继续递归建立子树
rebuild(root.left,preorder,inorder,rootIndex+1,left,rootInOrderIndex-1);
}
//右子树
if (rightTreeSize > 0){
TreeNode rightNode = new TreeNode(preorder[rootIndex+leftTreeSize+1]);
root.right = rightNode;
rebuild(root.right,preorder,inorder,rootIndex+leftTreeSize+1,rootInOrderIndex+1,right);
}
}
}
}
本文来自博客园,作者:HumorChen99,转载请注明原文链接:https://www.cnblogs.com/HumorChen/p/18039556