二叉树day8

105. 从前序与中序遍历序列构造二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

//迭代
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        //前序第一个结点是根结点
        TreeNode root = new TreeNode(preorder[0]);
        //结点数
        int n = preorder.length;
        //处理前序遍历
        Deque<TreeNode> stack = new LinkedList<>();
        //处理中序遍历 下标0为根节点最左下结点
        int index = 0;
        //根节点入栈,开始构造树
        stack.push(root);
        for (int i = 1; i < n; i++) {
            /*
             * 1. i是我们即将处理的前序遍历结点的下标
             * 2. root左子树不为空的情况下,inorder[index]是栈顶结点的最左子结点(
             * 3. root左子树不为空成立的情况下,从前序遍历某个结点开始处理时,这个结点位置到与inorder[index]
                 相等的位置是该结点到它的最左子结点,由前序遍历的顺序,在这段序列中,包括的是该结点的不包括任何右儿子的左子树,
                 由此可以构造先将该结点一直往左构造
             * 4.当栈顶与inorder[index]相等,说明无左子结点,当前处理结点便是前面构造不包括右儿子的左子树中的一个右儿子
             */
            //构造左树左结点
            TreeNode cur = stack.peek();
            if (inorder[index] != cur.val) {
                cur.left = new TreeNode(preorder[i]);
                //System.out.println("left:"+ cur.val + " " + cur.left.val);
                stack.push(cur.left);
            } else {
                //相等,当前处理结点是某个右儿子
                //处理中序遍历的结果,index++并抛出栈顶结点,若inorder[index]与栈顶结点值不等的话,那么这个结点就是右儿子的父结点
                while(!stack.isEmpty() && stack.peek().val == inorder[index]) {
                    cur = stack.pop();
                    index++;
                }
                //这里的话就已经把处理结点的父节点到其最左子结点中的结点全部弹出去了
                cur.right = new TreeNode(preorder[i]);
                //System.out.println("right:"+ cur.val + " " + cur.right.val + " index:" + index);
                stack.push(cur.right);
            }
        }
        return root;
        
    }
}

//递归 哈希 优化查找根节点在中序遍历的位置
// class Solution {
//     private Map<Integer, Integer> hashRoot;
//     public TreeNode buildTree(int[] preorder, int[] inorder) {
//         //构造中序遍历的hashmap key:值 value:下标
//         hashRoot = new HashMap<>();
//         int n = inorder.length;
//         for (int i = 0; i < n; i++) {
//             hashRoot.put(inorder[i], i);
//         }

//         return build(preorder, 0, preorder.length, inorder, 0, preorder.length);
//     }
//     private TreeNode build(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
//         //函数结束语句 如果要构造的数为空 或只有一个结点了
//         if (preEnd - preStart < 1) return null;
//         if (preEnd - preStart == 1) return new TreeNode(preorder[preStart]);
//         //不满足上述情况,需要继续构造子树

//         //当前构造过程 树的根节点 值为前序遍历结果集的第一个 注意是从这里开始编写build函数的
//         int rootVal = preorder[preStart];
//         TreeNode root = new TreeNode(rootVal);
//         //找到中序遍历中根节点下标
//         int i = hashRoot.get(rootVal);
//         //知道了前序遍历中左子树长度 可以知道中序遍历左子树的区间[inStart, i)和左子树长度 i - inStart
//         //知道左子树和右子树在前序遍历和中序遍历中的区间后,构造当前树的左右子树 区间左闭右开
//         root.left = build(preorder, preStart + 1, preStart + 1 + (i - inStart), inorder, inStart, i);
//         root.right = build(preorder, preStart + 1 + (i - inStart), preEnd, inorder, i + 1, inEnd);
//         return root;
//     }
// }

//递归 区间划分子树
// class Solution {
//     public TreeNode buildTree(int[] preorder, int[] inorder) {
//         return build(preorder, 0, preorder.length, inorder, 0, preorder.length);
//     }
//     private TreeNode build(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
//         //函数结束语句 如果要构造的数为空 或只有一个结点了
//         if (preEnd - preStart < 1) return null;
//         if (preEnd - preStart == 1) return new TreeNode(preorder[preStart]);
//         //不满足上述情况,需要继续构造子树

//         //当前构造过程 树的根节点 值为前序遍历结果集的第一个 注意是从这里开始编写build函数的
//         int rootVal = preorder[preStart];
//         TreeNode root = new TreeNode(rootVal);
//         //找到中序遍历中根节点,区分左右子树 i为中序遍历中根节点下标 区间左闭右开
//         int i;
//         for (i = 0; i < inEnd; i++) {
//             if (inorder[i] == rootVal) break;
//         }
//         //知道了前序遍历中左子树长度 可以知道中序遍历左子树的区间[inStart, i)和左子树长度 i - inStart
//         //知道左子树和右子树在前序遍历和中序遍历中的区间后,构造当前树的左右子树 区间左闭右开
//         root.left = build(preorder, preStart + 1, preStart + 1 + (i - inStart), inorder, inStart, i);
//         root.right = build(preorder, preStart + 1 + (i - inStart), preEnd, inorder, i + 1, inEnd);
//         return root;
//     }
// }

 

654. 最大二叉树

直接递归模拟构造树的过程

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

//递归
class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        int n = nums.length;
        if (n == 0) return null;
        //存储最大值下标
        int maxIndex = 0;
        for (int i = 1; i < n; i++) {
            maxIndex = nums[maxIndex] > nums[i] ? maxIndex : i;
        }
        return build(nums, 0, n, maxIndex);
    }
    //区间左闭右开
    private TreeNode build(int[] nums, int s, int e, int maxIndex) {
        TreeNode root = new TreeNode(nums[maxIndex]);
        if (maxIndex == s) root.left = null;
        else {
            //存储最大值下标
            int maxIndexLeft = s;
            for (int i = 1 + s; i < maxIndex; i++) {
                maxIndexLeft = nums[maxIndexLeft] > nums[i] ? maxIndexLeft : i;
            }
            root.left = build(nums, s, maxIndex, maxIndexLeft);
        }
        if (maxIndex == e - 1) root.right = null;
        else {
            //存储最大值下标
            int maxIndexRight = maxIndex + 1;
            for (int i = maxIndex + 2; i < e; i++) {
                maxIndexRight = nums[maxIndexRight] > nums[i] ? maxIndexRight : i;
            }
            root.right = build(nums, maxIndex + 1, e, maxIndexRight);
        }
        return root;
    }
}

可以看到我们分别求了根节点结点和左子树根节点和右子树根节点(如果有的话),非常冗余,可以将这一步放到构造树的函数的开头处理,我们只传入区间下标就可以了。然后将子树为空和子树只有一个结点的情况抽离处理,精简版代码如下:

//递归
class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return build(nums, 0, nums.length);
    }
    //区间左闭右开
    private TreeNode build(int[] nums, int s, int e) {
        //无元素和有一个元素
        if (e - s < 1) return null;
        if (e - s == 1) return new TreeNode(nums[s]);
        //否则继续构造
        //存储最大值下标
        int maxIndex = s;
        for (int i = 1 + s; i < e; i++) {
            maxIndex = nums[maxIndex] > nums[i] ? maxIndex : i;
        }
        TreeNode root = new TreeNode(nums[maxIndex]);
        root.left = build(nums, s, maxIndex);
        root.right = build(nums, maxIndex + 1, e);
        return root;
    }
}

刷了这么多树的题目后,感觉思路以及很清晰了哈哈。

参考:https://programmercarl.com

posted @   一梦两三年13  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示