leetcode—二叉树进阶2

1 二叉树的右视图

示例 1:

输入: [1,2,3,null,5,null,4]
输出: [1,3,4]

法一:

思路:根 右 左遍历二叉树 

—> 保证每层最先访问到都是最右边的节点

    // 二叉树的右视图 法一:根左右 遍历二叉树 每个深度第一个出现的即为右视图看到的
    static List<Integer> result = new ArrayList<>();

    public static List<Integer> rightView(TreeNode root) {
        postOrder(root, 0);
        return result;
    }

    public static void postOrder(TreeNode root, int depth) {
        if (root == null) {
            return;
        }
        if (depth == result.size()) {
            result.add(root.val);
        }
        depth++;
        postOrder(root.right, depth);
        postOrder(root.left, depth);
    }

法二:

思想:层序遍历

记录每一层的最后一个元素

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        // 创建列表 用于返回最终结果
        List<Integer> result = new ArrayList<>();
        // 判空
        if(root == null){
            return result;
        }

        // 创建队列用于层序遍历
        Queue<TreeNode> queue = new LinkedList<>();
        // 入队
        queue.offer(root);

        // 当队列不为空时 层序遍历二叉树
        while(!queue.isEmpty()){
            int currentSize = queue.size();
            for(int i = 0; i < currentSize; i++){
                // 出队
                TreeNode node = queue.poll();
                // 左右节点不为空时 入队
                if(node.left != null){
                    queue.offer(node.left);
                }
                if(node.right != null){
                    queue.offer(node.right);
                }
                // 若为当前层最后一个元素 将其值添加到列表中
                if(i == currentSize-1){
                    result.add(node.val);
                }
            }
        }
        return result;
    }

2 二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同。

示例 1:

输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]

思想:

  1. 先序遍历将结果存入列表中
  2. 然后在将列表中每一个节点的左子树设置为null ,右子树设置为下一个节点
class Solution {
    public void flatten(TreeNode root) {
        List<TreeNode> list = new ArrayList<TreeNode>();
        preOrder(root,list);

        int size = list.size();
        // 一定要从1开始遍历 0索引无前驱
        for(int i = 1; i < size; i++){
            // 得到当前节点 和当前节点的前一个和后一个节
            TreeNode prev = list.get(i-1);
            TreeNode curr = list.get(i);
            //将上一个节点的左子树设为 null          
            prev.left = null;
            // 将上一个节点的右子树设为当前节点
            prev.right = curr;
        }

    }
    // 二叉树的先序遍历  将遍历到的结果添加到列表中
    public void preOrder(TreeNode root, List<TreeNode> list){
        if(root != null){
            list.add(root);
            preOrder(root.left, list);
            preOrder(root.right, list);
        }
    }
}

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

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

方法一

思想:递归

注意点:每次递归构造二叉树的上界和下界

步骤:

  • 构造递归函数bulidMyTree

        先判断是否为空的二叉树  若左边界大于右边界 则直接返回null

  1. 找根节点对应的索引(先序遍历中第一个即为根节点),根据先序找到中序遍历中根节点的位置
  2. 创建根节点
  3. 求左子树节点的数目  (用于定位先序遍历中左右子树的边界)
  4. 递归调用函数 构造根节点的左右子树
  • 主函数
  1.  获取数组的长度(先序 中序数组中任意一个)
  2. 遍历数组,将中序遍历 值-索引  映射到哈希表 key-value  中 
  3. 调用递归函数构造二叉树
class Solution {
    Map<Integer,Integer> indexMap = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        // 构造中序遍历的哈希映射
        for(int i=0; i < n; i++){
            indexMap.put(inorder[i], i);
        }
        return buildMyTree(preorder, inorder, 0, n-1, 0, n-1);



    }

    // 递归函数 递归的构造二叉树
    public TreeNode buildMyTree(int[] preorder, int[] inorder, int pre_left, int pre_right, int in_left, int in_right)  {
        // 若左边界大于右边界 则二叉树为空 直接返回null
        if(pre_left > pre_right){
            return null;
        }
        // 找根节点 在先序 和中序中的索引(下标值)
        int pre_root = pre_left;
        int in_root = indexMap.get(preorder[pre_root]);

        // 建立根节点
        TreeNode root = new TreeNode(preorder[pre_root]);

        // 计算左子树 右子树节点 个数
        int left_treeSize = in_root - in_left;
        int right_treeSize = in_right - in_root;

        // 递归构造根节点的左子树和右子树  注意构造二叉树时 左右 节点的边界值
        // 先序遍历中「从 左边界+1 开始到 pre_left + left_treeSize」个元素
        // 对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root.left = buildMyTree(preorder, inorder,
                                pre_left + 1, pre_left + left_treeSize,
                                in_left, in_root-1);
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素对应了
        // 中序遍历中「从 根节点定位+1 到 右边界」的元素
        root.right = buildMyTree(preorder, inorder,
                                pre_left + left_treeSize + 1, pre_right, 
                                in_root + 1, in_right);

        // 返回递归构建的二叉树
        return root;
    }
}

方法二

思想:借助栈

  1. 用一个栈和一个指针辅助进行二叉树的构造。初始时栈中存放了根节点,指针指向中序遍历中的第一个节点
  2. 依次遍历前序遍历数组(从1开始) 如果index等于栈顶节点 不断的弹出栈顶节点并向右移动指针,将当前节点作为最后一个弹出的节点的右儿子
  3. 如果不同,把档期那节点当做栈顶节点的左儿子
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        // 若数组为空 证明二叉树为null 
        if(preorder == null || preorder.length == 0){
            return null;
        }
        // 创建根节点 为先序遍历的第一个节点
        TreeNode root = new TreeNode(preorder[0]);
        // 借助栈 构造二叉树
        Deque<TreeNode> stack = new LinkedList<>();
        // 根节点入队
        stack.push(root);
        int inorderIndex = 0;   // 初始化指针指向中序遍历的第一个值 索引
        for(int i = 1; i < preorder.length; i++){
            int preorderVal = preorder[i];
            // 获取栈顶元素
            TreeNode node = stack.peek();
            // 如果当前栈顶节点的值不等于中序遍历中的值
            if(node.val != inorder[inorderIndex]){
                // 创建左子节点 并入栈
                node.left = new TreeNode(preorderVal);
                stack.push(node.left);
            }else{
                // 当栈不为空会这栈顶值与当前中序遍历的值相等时
                while(! stack.isEmpty() && stack.peek().val == inorder[inorderIndex]){
                    node = stack.pop();
                    inorderIndex++;
                }
                // 右节点入栈
                node.right = new TreeNode(preorderVal);
                stack.push(node.right);
            }
        }
        return root;
    }
}

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值