LeetCode

101. 对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

思路:
① 拷贝一棵二叉树,再反转,将反转以后的二叉树和自己比较,看看是否相等。
② 设置一个辅助函数,递归去判断两棵子树是否镜面对称。
③ 还可以使用辅助数据结构“双端队列”完成。使用队列,并且是双端队列(链表实现)这个辅助数据结构。

  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode(int x) { val = x; }
  8. * }
  9. */
  10. class Solution {
  11. public boolean isSymmetric(TreeNode root) {
  12. if (root == null) return true;
  13. return isSym(root.left, root.right);
  14. }
  15. private boolean isSym(TreeNode left, TreeNode right) {
  16. if (left == null && right == null) return true;
  17. if ( (left == null && right != null) || (left != null && right == null) || left.val != right.val) return false;
  18. return isSym(left.left, right.right) && isSym(left.right, right.left);
  19. }
  20. }
  1. class Solution {
  2. public boolean isSymmetric(TreeNode root) {
  3. if (root == null) return true;
  4. Queue<TreeNode> q1 = new LinkedList<>();
  5. Queue<TreeNode> q2 = new LinkedList<>();
  6. q1.offer(root.left);
  7. q2.offer(root.right);
  8. while (!q1.isEmpty() && !q2.isEmpty()) {
  9. TreeNode node1 = q1.poll();
  10. TreeNode node2 = q2.poll();
  11. if (node1 == null && node2 == null) continue;
  12. if((node1 != null && node2 == null) || (node1 == null && node2 != null)) return false;
  13. if (node1.val != node2.val) return false;
  14. q1.offer(node1.left);
  15. q1.offer(node1.right);
  16. q2.offer(node2.right);
  17. q2.offer(node2.left);
  18. }
  19. return true;
  20. }
  21. }

110. 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:

一个二叉树每个结点 的左右两个子树的高度差的绝对值不超过1。

思路:
对于每一个结点,我们通过checkDepth方法递归获得左右子树的深度。如果子树是平衡的,则返回真实的深度,若不平衡,直接返回-1。

  1. class Solution {
  2. public boolean isBalanced(TreeNode root) {
  3. if(checkDepth(root) == -1) return false;
  4. else return true;
  5. }
  6. public int checkDepth(TreeNode root) {
  7. if(root == null) return 0;
  8. int left = checkDepth(root.left);
  9. if(left == -1) return -1;
  10. int right = checkDepth(root.right);
  11. if(right == -1) return -1;
  12. int dif = Math.abs(left - right);
  13. if(dif > 1) return -1;
  14. else return 1 + Math.max(left, right);
  15. }
  16. }

104. 二叉树的最大深度

给定一个二叉树,找出其最大深度。
二叉树的深度为根结点到最远叶子结点的最长路径上的结点数。
说明: 叶子结点是指没有子结点的结点。

思路:
使用递归的方法,考虑清楚递归终止条件,解答是非常简单且清楚的。其实本质上是“后序遍历”,从叶子结点开始计算深度。从底向上。写法其实上是二叉树的后序遍历(先左右子树,然后再自己)

  1. class Solution {
  2. public int maxDepth(TreeNode root) {
  3. if(root == null) return 0;
  4. return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
  5. }
  6. }

111. 二叉树的最小深度

给定一个二叉树,找出其最小深度。
最小深度是从根结点到最近叶子结点的最短路径上的结点数量。**

思路:
和最大深度类似,但是左右孩子有一个为空的时候,要分类判断。

  1. class Solution {
  2. public int minDepth(TreeNode root) {
  3. if(root==null){
  4. return 0;
  5. }
  6. if(root.left==null&&root.right==null){
  7. return 1;
  8. }
  9. if(root.left==null){
  10. return minDepth(root.right)+1;
  11. }
  12. if(root.right==null){
  13. return minDepth(root.left)+1;
  14. }
  15. return Math.min(minDepth(root.left),minDepth(root.right))+1;
  16. }
  17. }
class Solution {
    public int minDepth(TreeNode root) {
        if (root == null) return 0;
        int res = 0;
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.offer(root);
        while (!q.isEmpty()) {
            ++res;
            for (int i = q.size(); i > 0; --i) {
                TreeNode t = q.poll();
                if (t.left == null && t.right == null) return res;
                if (t.left != null) q.offer(t.left);
                if (t.right != null) q.offer(t.right);
            }
        }
        return -1;
    }
}

226. 翻转二叉树

翻转一棵二叉树。

思路:
使用反转,前序遍历后序遍历广度优先遍历都可以实现。

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root != null){
            TreeNode temp = root.left;
            root.left = root.right;
            root.right = temp;
            invertTree(root.left);
            invertTree(root.right);
        }
        return root;
    }
}

100. 相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且结点具有相同的值,则认为它们是相同的。

思路:
利用递归解决。和对称二叉树类似。

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p == null && q == null) return true;
        if ( (p != null && q == null) || (p == null && q != null) || (p.val != q.val) ) return false;
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}

404. 左叶子之和

计算给定二叉树的所有左叶子之和。

思路:
找递归关系是关键。需要判断是不是左叶子结点,如果是则算上该结点的值。非递归写法用BFS(todo)。

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if (root == null) {
            return 0;
        }

        if (root.left != null && root.left.right == null && root.left.left == null) {
            return root.left.val + sumOfLeftLeaves(root.right);
        }

        return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
    }
}

练习:

222. 完全二叉树的结点个数

剑指 Offer

剑指 Offer 07. 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

思路:
①递归。根据图中数组确定下一步的参数。
LeetCode 重建二叉树.png

/**
 * 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) {
        return helper(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }

    private TreeNode helper(int[] preorder,int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
        if (preStart > preEnd || inStart > inEnd) {
            return null;
        }
        TreeNode root = new TreeNode(preorder[preStart]);
        for (int i = 0; i < inorder.length; i++) {
            if (root.val == inorder[i]) {
                root.left = helper(preorder, preStart + 1, i - inStart + preStart, inorder, inStart, i - 1);
                root.right = helper(preorder, i - inStart + preStart + 1, preEnd, inorder, i + 1, inEnd);
                break;
            }
        }
        return root;
    }
}

②递归。map记录节点信息,更快。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    HashMap<Integer, Integer> dic = new HashMap<>();
    int[] po;

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        for (int i = 0; i < inorder.length; i++) {
            // 根节点在中序遍历中的位置
            dic.put(inorder[i], i);
        }
        po = preorder;

        return recur(0, 0, inorder.length - 1);
    }

    /**
     * @param preRoot 前序遍历中根节点的索引
     * @param inLeft  中序遍历的左边界
     * @param inRight 中序遍历的右边界
     *
     * @return 返回 root,含义是当前递归层级建立的根节点 root 为上一递归层级的根节点的左或右子节点
     */
    private TreeNode recur(int preRoot, int inLeft, int inRight) {
        if (inLeft > inRight) {
            return null;
        }

        // 建立当前根节点
        TreeNode root = new TreeNode(po[preRoot]);

        Integer inMid = dic.get(po[preRoot]);
        // 构造左子树
        root.left = recur(preRoot + 1, inLeft, inMid - 1);
        // 构造右子树
        root.right = recur(preRoot + inMid - inLeft + 1, inMid + 1, inRight);

        return root;
    }
}

③非递归。BFS。

/**
 * 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 == null || preorder.length == 0) {
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);
        int length = preorder.length;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);
        int inorderIndex = 0;
        for (int i = 1; i < 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;
    }
}

剑指 Offer 26. 树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。

思路:
递归。当A和B值相等时,通过helper()函数判断他们的子树是否有符合的,有符合的返回true,没有符合的接着判断A和B的值。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if (B == null || A == null) {
            return false;
        }
        if (A.val == B.val && (helper(A.left, B.left) && helper(A.right, B.right))) {
            return true;
        }
        return isSubStructure(A.left, B) || isSubStructure(A.right, B);
    }

    private boolean helper(TreeNode root1, TreeNode root2) {
        if (root2 == null) {
            return true;
        }
        if (root1 == null) {
            return false;
        }
        if (root1.val == root2.val) {
            return helper(root1.left, root2.left) && helper(root1.right, root2.right);
        } else {
            return false;
        }
    }
}

剑指 Offer 27. 二叉树的镜像

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

思路:
①二叉树。递归。

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if (root == null) return null;
        TreeNode tmp = root.left;
        root.left = mirrorTree(root.right);
        root.right = mirrorTree(tmp);
        return root;
    }
}

②利用辅助栈。

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null) return null;
        Stack<TreeNode> stack = new Stack<>() {{ add(root); }};
        while(!stack.isEmpty()) {
            TreeNode node = stack.pop();
            if(node.left != null) stack.add(node.left);
            if(node.right != null) stack.add(node.right);
            TreeNode tmp = node.left;
            node.left = node.right;
            node.right = tmp;
        }
        return root;
    }
}

剑指 Offer 28. 对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

思路:
二叉树。助手函数递归实现。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }
        return helper(root.left, root.right);
    }

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

剑指 Offer 37. 序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树。

思路:
①深度优先搜索。前序遍历。”#”代表空节点,用”,”将节点隔开。

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

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        return rSerialize(root, "");
    }

    private String rSerialize(TreeNode root, String str) {
        if (root == null) {
            str += "#,";
        } else {
            str += String.valueOf(root.val) + ",";
            str = rSerialize(root.left, str);
            str = rSerialize(root.right, str);
        }
        return str;
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        String[] arr = data.split(",");
        List<String> list = new ArrayList<>(Arrays.asList(arr));
        return rDeserialize(list);
    }

    private TreeNode rDeserialize(List<String> list) {
        if (list.get(0).equals("#")) {
            list.remove(0);
            return null;
        }

        TreeNode root = new TreeNode(Integer.valueOf(list.get(0)));
        list.remove(0);
        root.left = rDeserialize(list);
        root.right = rDeserialize(list);

        return root;
    }
}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));

②广度优先搜索(更快)。

public class Codec {
    public String serialize(TreeNode root) {
        if(root == null) return "[]";
        StringBuilder res = new StringBuilder("[");
        Queue<TreeNode> queue = new LinkedList<>() {{ add(root); }};
        while(!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if(node != null) {
                res.append(node.val + ",");
                queue.add(node.left);
                queue.add(node.right);
            }
            else res.append("null,");
        }
        res.deleteCharAt(res.length() - 1);
        res.append("]");
        return res.toString();
    }

    public TreeNode deserialize(String data) {
        if(data.equals("[]")) return null;
        String[] vals = data.substring(1, data.length() - 1).split(",");
        TreeNode root = new TreeNode(Integer.parseInt(vals[0]));
        Queue<TreeNode> queue = new LinkedList<>() {{ add(root); }};
        int i = 1;
        while(!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if(!vals[i].equals("null")) {
                node.left = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(node.left);
            }
            i++;
            if(!vals[i].equals("null")) {
                node.right = new TreeNode(Integer.parseInt(vals[i]));
                queue.add(node.right);
            }
            i++;
        }
        return root;
    }
}

剑指 Offer 55 - I. 二叉树的深度

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

思路:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:39.5 MB, 在所有 Java 提交中击败了100.00%的用户

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
    }
}

剑指 Offer 55 - II. 平衡二叉树

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

思路:
对于每一个节点,我们通过checkDepth方法递归获得左右子树的深度。如果子树是平衡的,则返回真实的深度,若不平衡,直接返回-1。
执行用时:1 ms, 在所有 Java 提交中击败了99.91%的用户
内存消耗:39.7 MB, 在所有 Java 提交中击败了100.00%的用户

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if (checkDepth(root) == -1) {
            return false;
        }
        return true;
    }

    private int checkDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = checkDepth(root.left);
        if (left == -1) {
            return -1;
        }
        int right = checkDepth(root.right);
        if (right == -1) {
            return -1;
        }
        int dif = Math.abs(left - right);
        if (dif > 1) {
            return -1;
        } else {
            return 1 + Math.max(left, right);
        }
    }
}

思路2:
获取高度,进行判断。

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        }
        if (Math.abs(getHeight(root.left) - getHeight(root.right)) > 1) {
            return false;
        } else {
            return isBalanced(root.left) && isBalanced(root.right);
        }
    }

    private int getHeight(TreeNode node) {
        if (node == null) {
            return 0;
        }
        return 1 + Math.max(getHeight(node.left), getHeight(node.right));
    }
}

剑指 Offer 68 - II. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

思路:
后序遍历。
执行用时:7 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:41.8 MB, 在所有 Java 提交中击败了100.00%的用户

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || p == root || q == root) {
            return root;
        }

        // 后序遍历
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left != null && right != null) {
            return root;
        }
        if (left == null ) {
            return right;
        }
        return left;
    }
}