LeetCode刷题:树2

后序遍历:状态转移与结果保存类型,需要集齐两个子节点信息来决定父节点信息。
前序遍历:前置拆分与处理
中序遍历:有序二叉树的处理

重建二叉树:前序遍历

在这里插入图片描述
对于任意一颗树而言,前序遍历的形式总是
[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]

即根节点总是前序遍历中的第一个节点。而中序遍历的形式总是
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。

考虑使用哈希表来帮助我们快速地定位根节点。对于哈希映射中的每个键值对,键表示一个元素(节点的值),值表示其在中序遍历中的出现位置。在构造二叉树的过程之前,我们可以对中序遍历的列表进行一遍扫描,就可以构造出这个哈希映射。在此后构造二叉树的过程中,我们就只需要 O(1)O(1) 的时间对根节点进行定位了。

https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-by-leetcode-s/

也是三种遍历的方法,这里用的是前序遍历
因为每一次都是先确认根节点,然后再去确认左右孩子。

class Solution {
    private Map<Integer, Integer> indexMap;
 
    public TreeNode myBuildTree(int[] preorder, int[] inorder, 
    int preorder_left, int preorder_right, 
    int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return null;
        }
 
        // 前序遍历中的第一个节点就是根节点
        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = indexMap.get(preorder[preorder_root]);        
        // 先把根节点建立出来
        TreeNode root = new TreeNode(preorder[preorder_root]);
 
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root - inorder_left;
        // 递归地构造左子树,并连接到根节点
        // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        // 通过root.left的方式直接连接即可
        root.left = myBuildTree(preorder, inorder, preorder_left + 1, 
        preorder_left + size_left_subtree, inorder_left, 
        inorder_root - 1);
        // 递归地构造右子树,并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root.right = myBuildTree(preorder, inorder, 
        preorder_left + size_left_subtree + 1, preorder_right, 
        inorder_root + 1, inorder_right);
        return root;
    }
 
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        // 构造哈希映射,帮助我们快速定位根节点
        indexMap = new HashMap<Integer, Integer>();
        for (int i = 0; i < n; i++) {
            indexMap.put(inorder[i], i);
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
}
class Solution {
    TreeNode res = new TreeNode();

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder.length == 0 || inorder.length == 0) {
            return null;
        }
        List<Integer> preorderList = Arrays.stream(preorder).boxed().collect(Collectors.toList());
        List<Integer> inorderList = Arrays.stream(inorder).boxed().collect(Collectors.toList());
        dfs(preorderList, inorderList, res);
        return res;
    }

    private void dfs(List<Integer> preorder, List<Integer> inorder, TreeNode cur) {
        if(preorder == null || preorder.size() == 0 || inorder.size() == 0) {
            return;
        }
        if(preorder.size() == 1) {
            cur.val = preorder.get(0);
            return;
        }
        cur.val = preorder.get(0);
        // inorder
        List<Integer> leftIn = new ArrayList<>();
        List<Integer> rightIn = new ArrayList<>();
        getInOrderList(inorder, leftIn, rightIn, cur.val);

        // preorder
        List<Integer> leftPre = new ArrayList<>();
        List<Integer> rightPre = new ArrayList<>();
        getPreOrderList(preorder, leftPre, rightPre, leftIn.size());

        if(leftIn.size() != 0) {
            cur.left = new TreeNode();
            dfs(leftPre, leftIn, cur.left);
        } else {
            cur.left = null;
        }
        if(rightIn.size() != 0) {
            cur.right = new TreeNode();
            dfs(rightPre, rightIn, cur.right);
        } else {
            cur.right = null;
        }
    }

    private void getInOrderList(List<Integer> inorder,
            List<Integer> leftIn, List<Integer> rightIn, int val) {
        boolean left = true;
        for(Integer tmp : inorder) {
            if(tmp == val) {
                left = false;
                continue;
            }
            if(tmp != val && left) {
                leftIn.add(tmp);
            } else {
                rightIn.add(tmp);
            }
        }
    }

    private void getPreOrderList(List<Integer> preorder,
            List<Integer> leftPre, List<Integer> rightPre, int size) {
        for(int i = 1; i < preorder.size(); i++) {
            if(i - 1 < size) {
                leftPre.add(preorder.get(i));
            } else {
                rightPre.add(preorder.get(i));
            }
        }
    }
}

中序遍历题目

在这里插入图片描述

两个节点被错误地交换后对原二叉搜索树造成了什么影响。对于二叉搜索树,我们知道如果对其进行中序遍历,得到的值序列是递增有序的,而如果我们错误地交换了两个节点,等价于在这个值序列中交换了两个值,破坏了值序列的递增性。

如果在一个递增的序列中交换两个值会造成什么影响。假设有一个递增序列 a=[1,2,3,4,5,6,7]a=[1,2,3,4,5,6,7]。如果我们交换两个不相邻的数字,例如 2 和 6,原序列变成了 a=[1,6,3,4,5,2,7],那么显然序列中有两个位置不满足 ai<ai+1,因此只要我们找到这两个位置,即可找到被错误交换的两个节点。
如果我们交换两个相邻的数字,例如 2 和 3,此时交换后的序列只有一个位置不满足 ai<ai+1。因此整个值序列中不满足条件的位置或者有两个,或者有一个。

思路:
中序遍历拿到所有元素
检查出现的乱序位置以及个数
DFS再次遍历树,交换两个元素值即可

class Solution {
    public void recoverTree(TreeNode root) {
        List<Integer> nums = new ArrayList<Integer>();
        inorder(root, nums);
        int[] swapped = findTwoSwapped(nums);
        recover(root, 2, swapped[0], swapped[1]);
    }
 
    public void inorder(TreeNode root, List<Integer> nums) {
        if (root == null) {
            return;
        }
        inorder(root.left, nums);
        nums.add(root.val);
        inorder(root.right, nums);
    }
 
    public int[] findTwoSwapped(List<Integer> nums) {
        int n = nums.size();
        int x = -1, y = -1;
        for (int i = 0; i < n - 1; ++i) {
            if (nums.get(i + 1) < nums.get(i)) {
                y = nums.get(i + 1);
                if (x == -1) {
                    x = nums.get(i);
                } else {
                    break;
                }
            }
        }
        return new int[]{x, y};
    }
 
    public void recover(TreeNode root, int count, int x, int y) {
        if (root != null) {
            if (root.val == x || root.val == y) {
                root.val = root.val == x ? y : x;
                if (--count == 0) {
                    return;
                }
            }
            recover(root.right, count, x, y);
            recover(root.left, count, x, y);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值