LeetCode——二叉树(Java)

二叉树

简介

记录一下自己刷题的历程以及代码。写题过程中参考了 代码随想录的刷题路线。会附上一些个人的思路,如果有错误,可以在评论区提醒一下。

Integer数组转TreeNode

用于在自己的编译器中传入参数调试代码

//根据Integer[]数组生成TreeNode
    public TreeNode createTreeNode(Integer[] array) {
        if (array.length == 0) return null;
        TreeNode root = new TreeNode(array[0]);
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int i = 1;
        while (i < array.length) {
            TreeNode temp = queue.poll();
            if (array[i] != null) {
                temp.left = new TreeNode(array[i]);
                queue.offer(temp.left);
            }
            i++;
            if (i < array.length && array[i] != null) {
                temp.right = new TreeNode(array[i]);
                queue.offer(temp.right);
            }
            i++;
        }
        return root;
    }

[简单] 144. 二叉树的前序遍历、94. 二叉树的中序遍历、145. 二叉树的后序遍历

144. 二叉树的前序遍历
94. 二叉树的中序遍历
145. 二叉树的后序遍历

前中后序遍历可以公用一套递归思路,都是比较经典的模板:

//先序遍历
递归访问(){
    对节点做操作
    递归访问(左子树)
    递归访问(右子树)
}

//中序遍历
递归访问(){
    递归访问(左子树)
    对节点做操作
    递归访问(右子树)
}

//后序遍历
递归访问(){
    递归访问(左子树)
    递归访问(右子树)
    对节点做操作
}
  1. 二叉树的前序遍历
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> answer = new ArrayList<>();
        preorder(root, answer);
        return answer;
    }

    public void preorder(TreeNode root, List<Integer> answer){
        if(root == null) return;
        answer.add(root.val);
        preorder(root.left,answer);
        preorder(root.right,answer);
        return;
    }
}
  1. 二叉树的中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> answer = new ArrayList<>();
        inorder(root, answer);
        return answer;
    }

    public void inorder(TreeNode root, List<Integer> answer){
        if(root == null) return;
        inorder(root.left,answer);
        answer.add(root.val);
        inorder(root.right,answer);
        return;
    }
}
  1. 二叉树的后序遍历
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> answer = new ArrayList<>();
        postorder(root, answer);
        return answer;
    }

    public void postorder(TreeNode root, List<Integer> answer){
        if(root == null) return;
        postorder(root.left,answer);
        postorder(root.right,answer);
        answer.add(root.val);
        return;
    }
}

二叉树层序遍历

[中等] 102. 二叉树的层序遍历

原题链接

经典的BFS
用队列保存树节点,每次统计队列的size(),也就是第n层节点数量。
处理这一层的节点,将其子节点全部加入队列,循环往复到队列为空。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        Queue<TreeNode> queue = new ArrayDeque<>();
        if(root == null) return ans;
        queue.add(root);
        while(!queue.isEmpty()){
            List<Integer> nums = new ArrayList<Integer>();
            int k = queue.size();
            while(k-- > 0) {
                TreeNode temp = queue.remove();
                nums.add(temp.val);
                if (temp.left != null) queue.add(temp.left);
                if (temp.right != null) queue.add(temp.right);
            }
            ans.add(nums);
        }
        return ans;
    }
}

[中等] 107. 二叉树的层序遍历 II

原题链接

方法①:
与102的最基本的层序遍历相似的逻辑,使用递归的方法把每次ans.add(nums);操作顺序进行了倒序。

class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        Queue<TreeNode> queue = new ArrayDeque<>();
        if(root == null) return ans;
        queue.add(root);
        recursion(queue, ans);
        return ans;
    }

    public void recursion(Queue<TreeNode> queue, List<List<Integer>> ans){
        if(!queue.isEmpty()){
            List<Integer> nums = new ArrayList<Integer>();
            int k = queue.size();
            while(k-- > 0) {
                TreeNode temp = queue.remove();
                nums.add(temp.val);
                if (temp.left != null) queue.add(temp.left);
                if (temp.right != null) queue.add(temp.right);
            }
            recursion(queue, ans);
            ans.add(nums);
        }
        return;
    }
}

方法②:
使用LinkedList,用头插法的方式构造返回值。(LinkedList底层为链表,头插法比较方便,ArrayList底层是连续存储,头插法复杂度为O(n))

class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> ans = new LinkedList<>();
        Queue<TreeNode> queue = new ArrayDeque<>();
        if(root == null) return ans;
        queue.add(root);
        while(!queue.isEmpty()){
            List<Integer> nums = new ArrayList<Integer>();
            int k = queue.size();
            while(k-- > 0) {
                TreeNode temp = queue.remove();
                nums.add(temp.val);
                if (temp.left != null) queue.add(temp.left);
                if (temp.right != null) queue.add(temp.right);
            }
            ans.add(0, nums);
        }
        return ans;
    }

}

[中等] 199. 二叉树的右视图

原题链接

与102的最基本的层序遍历相似的逻辑,构建返回值时每次只把当前层最右边的数加入ans即可。

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> ans = new ArrayList<Integer>();
        Queue<TreeNode> queue = new ArrayDeque<>();
        if(root == null) return ans;
        queue.add(root);
        while(!queue.isEmpty()){
            List<Integer> nums = new ArrayList<Integer>();
            int k = queue.size();
            while(k-- > 0) {
                TreeNode temp = queue.remove();
                nums.add(temp.val);
                if (temp.left != null) queue.add(temp.left);
                if (temp.right != null) queue.add(temp.right);
            }
            ans.add((nums.get(nums.size()-1)));
        }
        return ans;
    }
}

[简单] 637. 二叉树的层平均值

原题链接

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        Queue<TreeNode> queue = new ArrayDeque<>();
        List<Double> ans = new ArrayList<>();
        if(root == null) return ans;
        queue.add(root);
        while(!queue.isEmpty()){
            double num = 0;
            int k = queue.size();
            int i = k;
            while(k-- > 0){
                TreeNode temp = queue.remove();
                num += temp.val;
                if(temp.left != null) queue.add(temp.left);
                if(temp.right != null) queue.add(temp.right);
            }
            ans.add(num / i);
        }
        return ans;
    }
}

[中等] 429. N 叉树的层序遍历

原题链接

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        Queue<Node> queue = new ArrayDeque<>();
        if(root == null) return ans;
        queue.add(root);
        while(!queue.isEmpty()){
            List<Integer> nums = new ArrayList<Integer>();
            int k = queue.size();
            while(k-- > 0) {
                Node temp = queue.remove();
                nums.add(temp.val);
                for(int i = 0; i < temp.children.size(); i++) {
                    queue.add(temp.children.get(i));
                }
            }
            ans.add(nums);
        }
        return ans;
    }
}

[中等] 515. 在每个树行中找最大值

原题链接

class Solution {
    public List<Integer> largestValues(TreeNode root) {
       List<Integer> ans = new ArrayList<>();
        Queue<TreeNode> queue = new ArrayDeque<>();
        if(root == null) return ans;
        queue.add(root);
        while(!queue.isEmpty()){
            int max = queue.peek().val;
            int k = queue.size();
            while(k-- > 0) {
                TreeNode temp = queue.remove();
                max = max > temp.val ? max : temp.val;
                if(temp.left != null) queue.add(temp.left);
                if(temp.right != null) queue.add(temp.right);
            }
            ans.add(max);
        }
        return ans;
    }
}

[中等] 116. 填充每个节点的下一个右侧节点指针、[中等]117. 填充每个节点的下一个右侧节点指针 II

[中等] 116. 填充每个节点的下一个右侧节点指针
[中等]117. 填充每个节点的下一个右侧节点指针 II
方法①:
正常的层序遍历,每层除了最后一个节点之外都对next赋值

class Solution {
    public Node connect(Node root) {
       List<Integer> ans = new ArrayList<>();
        Queue<Node> queue = new ArrayDeque<>();
        if(root == null) return root;
        queue.add(root);
        while(!queue.isEmpty()){
            int k = queue.size();
            while(k-- > 0) {
                Node temp = queue.remove();
                if(k > 0) {
                    temp.next = queue.peek();
                }
                if(temp.left != null) queue.add(temp.left);
                if(temp.right != null) queue.add(temp.right);
            }
            
        }
        return root;
    }
}

方法②:
使用next链接来对同层次节点做遍历,可以省去队列的开销
注意Node引用需要申请在方法外,不能做参数传递,java中都是值传递

class Solution {
    Node last = null, nextStart = null;
    public Node connect(Node root) {
        List<Integer> ans = new ArrayList<>();
        if(root == null) return root;

        Node p = root;
        while(p!=null){
            if(p.left != null){
                handle(p.left);
            }
            if(p.right != null){
                handle(p.right);
            }
            p = p.next;
            if(p == null && nextStart != null){
                p = nextStart;
                nextStart = null;
                last = null;
            }
        }
        return root;
    }

    public void handle(Node p){
        if(nextStart == null){
            nextStart = p;
        }
        if(last != null){
            last.next = p;
        }
        last = p;
    }

}

[简单] 104. 二叉树的最大深度

原题链接

方法①:层序遍历

class Solution {
    public int maxDepth(TreeNode root) {
        int depth = 0;
        if(root == null) return depth;
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.add(root);
        while(!queue.isEmpty()){
            depth++;
            int k = queue.size();
            while(k-- > 0) {
                TreeNode temp = queue.remove();
                if (temp.left != null) queue.add(temp.left);
                if (temp.right != null) queue.add(temp.right);
            }
        }
        return depth;
    }
}

方法②:递归
树的高度就是 其子树的最大高度 + 1,用在多叉树上也是一样的思路

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1;
    }
}

[简单] 111. 二叉树的最小深度

原题链接
方法①:层序遍历找左右子树皆空的点即可

class Solution {
    public int minDepth(TreeNode root) {
        int depth = 0;
        if(root == null) return depth;
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.add(root);
        while(!queue.isEmpty()){
            depth++;
            int k = queue.size();
            while(k-- > 0) {
                TreeNode temp = queue.remove();
                if(temp.left == null && temp.right == null) return depth;
                if (temp.left != null) queue.add(temp.left);
                if (temp.right != null) queue.add(temp.right);
            }
        }
        return depth;
    }
}

方法②:递归求解
用递归求解记住需要的是到叶子节点的深度
如果非叶子节点,假设只有单边左子树,右子数应当是找不到叶子节点也就是距离无穷大,可以设置一个Integer.MAX_VALUE做为返回值,这样通过比较,递归的上一层就会获得左子树找到叶子节点的最小距离 + 1

class Solution {
    public int minDepth(TreeNode root) {
        if(root == null) return 0;
        if(root.left == null && root.right == null) return 1;
        int leftMin = Integer.MAX_VALUE;
        int rightMin = Integer.MAX_VALUE;
        if(root.left != null) leftMin = minDepth(root.left);
        if(root.right != null) rightMin = minDepth(root.right);
        return (leftMin < rightMin ? leftMin : rightMin) + 1;
    }
}

[简单] 226. 翻转二叉树

原题链接

前序遍历的基础上每一次遍历节点做翻转操作。
切记前序、后续、层次遍历都可以,但是不可以是中序遍历,因为中序遍历是左 中 右 的顺序,递归调整左子树之后,处理当前节点会把左右子树对调,这样进入右子数递归时其实还是对原先的左子树做操作。

class Solution {
    public TreeNode invertTree(TreeNode root) {
        preorder(root);
        return root;
    }

    public void preorder(TreeNode root){
        if(root == null) return;
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        preorder(root.left);
        preorder(root.right);
        return;
    }
}

[简单] 101. 对称二叉树

原题链接

经典的递归思路,对左右子树做反方向的递归即可,在左子树上做前序遍历,每次递归left的左节点时就去递归right的右节点,递归left的右节点时则递归right的左节点。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null) return true;
        TreeNode left = root.left;
        TreeNode right = root.right;
        return recursion(left, right);
    }

    public boolean recursion(TreeNode left, TreeNode right){
        if(left == null && right == null)
            return true;
        else if(left != null && right != null) {
            if (left.val != right.val)
                return false;
            if (recursion(left.left, right.right) && recursion(left.right, right.left))
                return true;
        }
        return false;
    }
}

[简单] 100. 相同的树

原题链接

两棵树同频做前序遍历即可,其他遍历方式也是ok的。

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        return recursion(p, q);
    }

    public boolean recursion(TreeNode p, TreeNode q){
        if(p == null && q == null)
            return true;
        else if(p != null && q != null) {
            if (p.val != q.val)
                return false;
            if (recursion(p.left, q.left) && recursion(p.right, q.right))
                return true;
        }
        return false;
    }
}

[简单] 572. 另一棵树的子树

原题链接

两层递归
①preorder:对root 的前序遍历,找到与subRoot相同值的节点,作为比较的起点②cmprecursion:对root的节点以及subRoot的根节点 做 同频前序遍历对比

class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(subRoot == null) return true;
        if(root == null) return true;
        return preorder(root, subRoot);
    }

    public boolean preorder(TreeNode p, TreeNode q){
        if(p == null) return false;
        if(p.val == q.val && cmprecursion(p, q))
            return true;
        if(preorder(p.left, q) || preorder(p.right, q)) 
            return true;
        return false;
    }

    public boolean cmprecursion(TreeNode p, TreeNode q){
        if(p == null && q == null) 
            return true;
        else if(p != null && q != null){
            if(p.val != q.val) 
                return false;
            if(cmprecursion(p.left, q.left) && cmprecursion(p.right, q.right))
                return true;
        }
        return false;
    }
}

[简单] 222. 完全二叉树的节点个数

原题链接

递归

class Solution {
    public int countNodes(TreeNode root) {
        if(root == null) return 0;
        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}

[简单] 110. 平衡二叉树

原题链接

递归,后序遍历
用-2标记 以当前节点为根节点的子树非平衡二叉树,在递归中一旦出现-2就层层传递到root,用来标识存在子树为非平衡二叉树

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(countDepth(root) == -2) return false;
        return true;
    }

    public int countDepth(TreeNode p){
        if(p == null) return 0;
        int leftDepth = countDepth(p.left);
        int rightDepth = countDepth(p.right);
        int flag = leftDepth - rightDepth;
        if(leftDepth == -2 || rightDepth == -2 || flag > 1 || flag < -1) return -2;
        else return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1;
    }
}

[简单] 257. 二叉树的所有路径

原题链接

思路就是DFS深度优先遍历找到每一条路径,效率差异主要体现在对字符串拼接的处理上,使用StringBuilder会更高效一些。
在这里插入图片描述

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> ans = new ArrayList<>();
        if(root == null) return ans;
        DFS(root, ans, "");
        return ans;
    }

    public void DFS(TreeNode p, List<String> ans, String string){
        StringBuilder sb = new StringBuilder(string);
        sb.append(p.val);
        if(p.left == null && p.right == null){
            ans.add(sb.toString());
            return;
        }
        sb.append("->");
        if(p.left != null)
            DFS(p.left, ans, sb.toString());
        if(p.right != null)
            DFS(p.right, ans, sb.toString());
    }
}

[简单] 404. 左叶子之和

原题链接

在前序遍历的基础上更改,用一个布尔类型标记当前节点是否是某个节点的左孩子

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        int sum = preorder(root, false, 0);
        return sum;
    }

    public int preorder(TreeNode root, boolean isLChild, int sum) {
        if (root == null) return sum;
        if (root.left == null && root.right == null && isLChild){
            return sum + root.val;
        }
        return preorder(root.left, true, sum) + preorder(root.right, false, sum);
    }
}

[中等] 513. 找树左下角的值

原题链接

最容易想到就是层序遍历,每层都记录 队列头的值即可

class Solution {
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new ArrayDeque<>();
        int ans = root.val;
        queue.add(root);
        while(!queue.isEmpty()){
            int k = queue.size();
            ans = queue.peek().val;
            while(k-- > 0) {
                TreeNode temp = queue.remove();
                if (temp.left != null) queue.add(temp.left);
                if (temp.right != null) queue.add(temp.right);
            }
        }
        return ans;
    }
}

方法②:稍微绕一些,使用前序遍历,每次碰到新的maxDepth,都是左子树先,所以,只要碰到新的最大深度就可以更新返回值

class Solution {
    int maxDepth = -1;
    int value = 0;

    public int findBottomLeftValue(TreeNode root) {
        preorder(root, 0);
        return value;
    }

    public void preorder(TreeNode node, int depth) {
        // 终止条件: 遇到叶子结点就可以return了。
        // 并且:当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度。
        if (node.left == null && node.right == null) {
            if (maxDepth < depth) {
                maxDepth = depth;
                value = node.val;
            }
            return;
        }

        // 递归遍历左子树
        if (node.left != null) {
            preorder(node.left, depth + 1);
        }

        // 递归遍历右子树
        if (node.right != null) {
            preorder(node.right, depth + 1);
        }
    }
}

[简单] 112. 路径总和

原题链接

前序遍历,记得总和与目标值相当时还需要判断是否是叶子节点,否则并不是一组答案

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        return preOrder(root, 0 ,targetSum);
    }

    public boolean preOrder(TreeNode root, int sum, int targetSum){
        if(root == null) return false;
        sum = sum + root.val;
        if(sum == targetSum && root.left == null && root.right == null)
            return true;
        if(preOrder(root.left, sum, targetSum)) return true;
        if(preOrder(root.right, sum, targetSum)) return true;
        return false;
    }
}

[中等] 113. 路径总和 II

原题链接

前序遍历,记得总和与目标值相当时还需要判断是否是叶子节点,否则并不是一组答案

class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> answer = new ArrayList<>();
        preOrder(root, ans, answer, 0, targetSum);
        return ans;
    }

    public void preOrder(TreeNode root, List<List<Integer>> ans, List<Integer> answer, int sum, int targetSum){
        if(root == null) return;
        sum = sum + root.val;
        answer.add(root.val);
        if(sum == targetSum && root.left == null && root.right == null)
            ans.add(new ArrayList<>(answer));
        preOrder(root.left, ans, answer, sum, targetSum);
        preOrder(root.right, ans, answer, sum, targetSum);
        answer.remove(answer.size() - 1);
    }
}

[中等] 106. 从中序与后序遍历序列构造二叉树

原题链接

递归思路解决
思路代码:

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        TreeNode ans = build(inorder, postorder);
        return ans;
    }

    public TreeNode build(int[] inorder, int[] postorder){
        if(postorder.length == 0) {
            return null;
        }
        int length = postorder.length;
        int del = postorder[length - 1];
        int i = 0;
        for(; i < length; i++){
            if(inorder[i] == del) break;
        }
        //获取inorder在i之前的元素
        int[] left = Arrays.copyOfRange(inorder, 0, i);
        TreeNode temp = new TreeNode();
        temp.val = del;
        temp.left = build(Arrays.copyOfRange(inorder, 0, i), Arrays.copyOfRange(postorder, 0, i));
        temp.right = build(Arrays.copyOfRange(inorder, i + 1, length), Arrays.copyOfRange(postorder, i, length - 1));
        return temp;
    }
}

在这里插入图片描述
提速,保留了一些日志打印方便观察运行结果:

class Solution {
    Map<Integer, Integer> indexMap = new HashMap<>();
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        TreeNode ans = new TreeNode();
        if(inorder.length == 0) return ans;
        for (int i = 0; i < inorder.length; i++) {
            indexMap.put(inorder[i], i);
        }
        ans = build(inorder, postorder, 0, inorder.length - 1, 0, postorder.length - 1);
        return ans;
    }

    public TreeNode build(int[] inorder, int[] postorder, int inStart, int inEnd, int postStart, int postEnd){
        System.out.println(inStart + "-" + inEnd + "   " + postStart + "-" + postEnd);
        if(postStart > postEnd) {
            return null;
        }
        int del = postorder[postEnd];
        int i = indexMap.get(del);
        TreeNode temp = new TreeNode();
        temp.val = del;
        System.out.println("left");
        temp.left = build(inorder, postorder, inStart, i - 1, postStart, postStart + i - inStart - 1);
        System.out.println("right");
        temp.right = build(inorder, postorder, i + 1, inEnd, postStart + i - inStart, postEnd - 1);
        return temp;
    }
}

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

原题链接

思路参考上一题,后序遍历中根节点在最后一个元素,前序遍历则在第一个元素。

class Solution {
    Map<Integer, Integer> indexMap = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        TreeNode ans = new TreeNode();
        if(inorder.length == 0) return ans;
        for (int i = 0; i < inorder.length; i++) {
            indexMap.put(inorder[i], i);
        }
        ans = build(inorder, preorder, 0, inorder.length - 1, 0, preorder.length - 1);
        return ans;
    }

    public TreeNode build(int[] inorder, int[] preorder, int inStart, int inEnd, int preStart, int preEnd){
        System.out.println(inStart + "-" + inEnd + "   " + preStart + "-" + preEnd);
        if(preStart > preEnd) {
            return null;
        }
        int del = preorder[preStart];
        int i = indexMap.get(del);
        TreeNode temp = new TreeNode();
        temp.val = del;
        int leftSize = i - inStart;
        System.out.println("left");
        temp.left = build(inorder, preorder, inStart, i - 1, preStart + 1, preStart + leftSize);
        System.out.println("right");
        temp.right = build(inorder, preorder, i + 1, inEnd, preStart + leftSize + 1, preEnd);
        return temp;
    }
}

[中等] 654. 最大二叉树

原题链接

有四句日志打印用来观察递归过程,最后提交需要去除,打印比较浪费时间,总体还是一个递归的思路,每次传入数组序列,找到当前节点的值,然后把序列的左右分别放入下一层递归建树。

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        TreeNode ans = new TreeNode();
        int length = nums.length;
        if(length == 0) return ans;
        ans = build(nums, 0, length - 1);
        return ans;
    }

    public TreeNode build(int[] nums, int start, int end){
        System.out.println(start + "-" + end);
        if(start > end) return null;
        int max = Integer.MIN_VALUE;
        int index = start;
        for(int i = start; i <= end; i++){
            if(nums[i] > max){
                max = nums[i];
                index = i;
            }
        }
        TreeNode temp = new TreeNode();
        temp.val = nums[index];
        System.out.println(temp.val);
        System.out.println("left");
        temp.left = build(nums, start, index - 1);
        System.out.println("right");
        temp.right = build(nums, index + 1, end);
        return temp;
    }
}

[简单] 617. 合并二叉树

原题链接

最开始想到的递归方法,其实已经效率比较高了,看了一些别人的答案之后,发现,如果root1 == null 而 root2 != null 其实可以直接返回root2,效率更高。不过在这题的示例中反正都是超过100%,可能是示例中的root1与root2结构还是比较相近的。

原思路:

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1 == null && root2 == null) return null;
        TreeNode temp = new TreeNode();
        if(root1 == null){
            temp.val = root2.val;
            temp.left = mergeTrees(root1, root2.left);
            temp.right = mergeTrees(root1, root2.right);
        }else if(root2 == null){
            temp.val = root1.val;
            temp.left = mergeTrees(root1.left, root2);
            temp.right = mergeTrees(root1.right, root2);
        }else{
            temp.val = root1.val + root2.val;
            temp.left = mergeTrees(root1.left, root2.left);
            temp.right = mergeTrees(root1.right, root2.right);
        }
        return temp;
    }
}

改进:

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        TreeNode temp = new TreeNode();
        if(root1 == null && root2 == null)
            return null;
        else if(root1 == null){
            return root2;
        }else if(root2 == null){
            return root1;
        }else{
            temp.val = root1.val + root2.val;
            temp.left = mergeTrees(root1.left, root2.left);
            temp.right = mergeTrees(root1.right, root2.right);
        }
        return temp;
    }
}

二叉搜索树

[简单] 700. 二叉搜索树中的搜索

原题链接

二叉搜索树最基本用法

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root == null) return null;
        if(val == root.val) return root;
        else if(val < root.val){
            return searchBST(root.left, val);
        }else {
            return searchBST(root.right ,val);
        }
    }
}

[中等] 98. 验证二叉搜索树

原题链接
核心思路就是中序遍历序列保持单调性,可以生成中序遍历数组然后做单调性检测。
效率高一点的方法就是不另外生成数组,在遍历过程中做单调性检测。

class Solution {
    TreeNode prev = null;
    public boolean isValidBST(TreeNode root) {
        return inorder(root);
    }

    //中序遍历
    public boolean inorder(TreeNode root){
        if(root == null) return true;
        //检查左子树
        if(!inorder(root.left)){
            return false;
        }
        //检查当前节点
        if(prev != null && root.val <= prev.val){
            return false;
        }
        prev = root;
        //检查右子树
        return inorder(root.right);
    }
}

[简单] 530. 二叉搜索树的最小绝对差

原题链接

获取中序遍历数组,遍历取出最大差值

class Solution {
    public int getMinimumDifference(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        inorder(root, ans);
        int min = Integer.MAX_VALUE;
        for(int i = 1; i < ans.size(); i++){
            int d = ans.get(i) - ans.get(i - 1);
            min = min < d ? min : d;
        }
        return min;
    }

    public void inorder(TreeNode root, List<Integer> ans){
        if(root == null) return;
        inorder(root.left, ans);
        ans.add(root.val);
        inorder(root.right, ans);
    }
}

[简单] 501. 二叉搜索树中的众数

原题链接

也可以使用中序遍历获取数组,通过遍历数组找出所有众数。
如下方法是把找数的过程集成到中序遍历过程中,没有使用额外空间。

在这里插入图片描述

class Solution {
    int maxValue = Integer.MIN_VALUE;
    TreeNode pre;
    int count;
    public int[] findMode(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        inorder(root, ans);
        int[] res = new int[ans.size()];
        for (int i = 0; i < ans.size(); ++i) {
            res[i] = ans.get(i);
        }
        return res;
    }

    public void inorder(TreeNode root, List<Integer> ans){
        if(root == null) return;
        inorder(root.left, ans);
        if(pre != null && pre.val != root.val){
            count = 1;
        }else{
            count++;
        }
        if(count == maxValue){
            ans.add(root.val);
        }else if(count > maxValue){
            maxValue = count;
            ans.clear();
            ans.add(root.val);
        }
        pre = root;
        inorder(root.right, ans);
    }
}

[中等] 236. 二叉树的最近公共祖先(引出235)

原题链接

方法①:dfs递归查找
一旦遇到p或者q,就将其返回,如果当前子树没有遇到p或者q,直接返回null。如果说pq的子节点,或者qp的子节点,则两个节点中的父节点就是公共最近父节点,则在if(root.val == p.val || root.val == q.val) return root;已经将其返回。
左右子树搜索的三种情况:
①节点左右子树均有返回值,则当前递归root节点就是最近的公共祖先,此时由于pq都是root的子节点,返回的每一层另一边得到的一定是nullroot能够顺利返回到最上层
②左右子树一个null一个有返回值,则当前递归的root节点所包含的子树只有p或者q其中一个目标节点,直接将目标节点返回给上一层判断。
③左右子树均返回null,则当前子树并不存在p或者q,返回null

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null;
        if(root.val == p.val || root.val == q.val) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p ,q);
        if(left != null && right != null) return root;
        else if(left == null) return right;
        else return left;
    }
}

方法②:dfs寻找路径
使用dfs寻找root到p和q的路径,路径肯定是以根节点的值作为起点,遍历两个路径数组,找到数值相同、索引值最大的节点,即为公共最近祖先。

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        List<TreeNode> ans1 = new ArrayList<>();
        List<TreeNode> ans2 = new ArrayList<>();
        dfs(ans1, root, p);
        dfs(ans2, root ,q);
        int size1 = ans1.size();
        int size2 = ans2.size();
        TreeNode temp = root;
        for(int i = 0; i < size1 && i < size2; i++){
            if(ans1.get(i) == ans2.get(i)) temp = ans1.get(i);
        }
        return temp;
    }

    public boolean dfs(List<TreeNode> ans, TreeNode root, TreeNode temp){
        if(root == null) return false;
        ans.add(root);
        if(root.val == temp.val) return true;
        if(dfs(ans, root.left, temp)) return true;
        if(dfs(ans, root.right, temp)) return true;
        ans.remove(ans.size() - 1);
        return false;
    }
}

[中等] 235. 二叉搜索树的最近公共祖先

原题链接

使用236的解法也能获得比较好的效率
如果要利用二叉搜索树的特性:其最近公共祖先的值一定在p与q的值中间,一旦找到一个节点的值在p与q构成的范围内,则其就是最近公共子节点。
二叉搜索树的中序遍历是保持单调性的,可以考虑一下,以如下示例中如果取p.val = 4,q.val = 8;
中序遍历为[0,2,3,4,5, 6, 7,8,9]
可以概括为[ 包含4的序列, 6, 包含8的序列]

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
        if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
        return root;
    }

[中等] 701. 二叉搜索树中的插入操作

原题链接

题目给的限制比较少,val的值都是独立的,只要根据搜索二叉树的排序特性,迭代找到插入位置即可,没有涉及到树结构的重构

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        TreeNode temp = root;
        TreeNode pre = null;
        while(temp != null){
            if(temp.val < val) {
                pre = temp;
                temp = temp.right;
            }
            else {
                pre = temp;
                temp = temp.left;
            }
        }
        if(pre == null) return new TreeNode(val);
        if(pre.val < val) pre.right = new TreeNode(val);
        else pre.left = new TreeNode(val);
        return root;
    }
}

[中等] 450. 删除二叉搜索树中的节点

原题链接

第一种情况:没找到删除的节点,遍历到空节点直接返回了
然后根据二叉搜索树的特性,寻找节点

找到节点:
第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

注意第五种情况中,如果对 删除节点的右子树最左节点右子树不为null,需要把他挂到父节点的左子树上。
在这里插入图片描述

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null)
            return root;
        if (key < root.val) {
            root.left = deleteNode(root.left, key);
        } else if (key > root.val) {
            root.right = deleteNode(root.right, key);
        } else {
            // 处理删除逻辑
            if(root.left == null && root.right == null)
                return null;
            else if(root.left == null)
                return root.right;
            else if(root.right == null)
                return root.left;
            else {
                TreeNode right = root.right;
                TreeNode temp = null;
                while(right.left != null) {
                    temp = right;
                    right = right.left;
                }
                right.left = root.left;
                //right不是直接挂在root的右边
                if(temp != null){
                    temp.left = right.right;
                    right.right = root.right;
                }
                return right;
            }
        }
        return root;
    }
}

[中等] 669. 修剪二叉搜索树

原题链接

public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root == null) return root;
        if(root.val < low){
            return trimBST(root.right, low, high);
        }else if(root.val > high){
            return trimBST(root.left, low, high);
        }else {
            root.left = trimBST(root.left, low, high);
            root.right = trimBST(root.right, low, high);
        }
        return root;
    }

[中等] 108. 将有序数组转换为二叉搜索树

原题链接

每一层递归找到当前序列中位数做根节点,左半序列进入下一层递归做左子树,右半序列进入下一层递归做右子数。

public TreeNode sortedArrayToBST(int[] nums) {
        TreeNode ans = createBST(nums, 0, nums.length - 1);
        return ans;
    }

    public TreeNode createBST(int[] nums, int begin, int end){
        if(end < begin) return null;
        else if(begin == end) return new TreeNode(nums[begin]);
        int div = (begin + end)/2;
        TreeNode temp = new TreeNode(nums[div]);
        temp.left = createBST(nums, begin, div - 1);
        temp.right = createBST(nums, div + 1, end);
        return temp;
    }

[中等] 538. 把二叉搜索树转换为累加树

原题链接

把中序遍历反过来,就能从最右边为起点递归这个树,用pre保存上一个节点,每次递归都把当前节点的值加上pre节点的值,

class Solution {
    TreeNode pre;
    public TreeNode convertBST(TreeNode root) {
        if(root == null) return null;
        root.right = convertBST(root.right);
        if(pre != null) root.val += pre.val;
        pre = root;
        root.left = convertBST(root.left);
        return root;
    }
}
  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值