Leetcode —「树」系列题解

二叉树的遍历

先序遍历

Leetcode - 144 Binary Tree Preorder Traversal (Medium)

解法一:递归

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> ansList = new ArrayList<Integer>();
    backtrack(ansList, root);
    return ansList;
}

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

解法二:遍历

public List<Integer> preorderTraversal(TreeNode node) {
    List<Integer> list = new LinkedList<Integer>();
    Stack<TreeNode> rights = new Stack<TreeNode>();
    while(node != null) {
        list.add(node.val);
        if (node.right != null) {
            rights.push(node.right);
        }
        node = node.left;
        if (node == null && !rights.isEmpty()) {
            node = rights.pop();
        }
    }
    return list;
}

中序遍历

Leetcode - 94 Binary Tree Inorder Traversal (Medium)

解法一:递归

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

解法二:遍历

public List<Integer> inorderTraversal(TreeNode root) {
  List<Integer> ans = new ArrayList<Integer>();
  Stack<TreeNode> stack = new Stack<TreeNode>();
  TreeNode cur = root;
  while(!stack.isEmpty() || cur != null){
      while(cur != null){
          stack.push(cur);
          cur = cur.left;
      }
      cur = stack.pop();
      ans.add(cur.val);
      cur = cur.right;
  }
  return ans;
}

后序遍历

Leetcode - 145 Binary Tree Postorder Traversal (Hard)

解法一:递归

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> ansList = new ArrayList<Integer>();
    backtrack(ansList, root);
    return ansList;
}

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

解法二:遍历

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> ans = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    if (root == null) return ans;
    stack.push(root);
    while (!stack.isEmpty()) {
        TreeNode cur = stack.pop();
        ans.addFirst(cur.val);
        if (cur.left != null) {
            stack.push(cur.left);
        }
        if (cur.right != null) {
            stack.push(cur.right);
        } 
    }
    return ans;
}

层次遍历

Leetcode - 102 Binary Tree Level Order Traversal (Medium)

Input: [3,9,20,null,null,15,7]
    3
   / \
  9  20
    /  \
   15   7
Output:
[
  [3],
  [9,20],
  [15,7]
]

解法一:递归

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> ans = new ArrayList<>();
    backtrack(ans, root, 0);
    return ans;
}
public void backtrack(List<List<Integer>> ans, TreeNode curNode, int level){ 
    if(curNode == null) return;
    if(ans.size() - 1 < level){
        ans.add(new ArrayList<Integer>());
    }
    ans.get(level).add(curNode.val);
    backtrack(ans, curNode.left, level + 1);
    backtrack(ans, curNode.right, level + 1);
}

解法二:遍历

public List<List<Integer>> levelOrder(TreeNode root) {
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    List<List<Integer>> wrapList = new LinkedList<List<Integer>>();
    if(root == null) return wrapList;
    queue.offer(root);
    while(!queue.isEmpty()){
        int levelNum = queue.size();
        List<Integer> subList = new LinkedList<Integer>();
        for(int i=0; i<levelNum; i++) {
            if(queue.peek().left != null) queue.offer(queue.peek().left);
            if(queue.peek().right != null) queue.offer(queue.peek().right);
            subList.add(queue.poll().val);
        }
        wrapList.add(subList);
    }
    return wrapList;
}

先序遍历系列

判断相同树

Leetcode 100 - Same Tree (Easy)

Input:     1         1
          / \       / \
         2   3     2   3

        [1,2,3],   [1,2,3]

Output: true

解法一:递归

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

解法二:遍历

public boolean isSameTree(TreeNode p, TreeNode q) {
    Stack<TreeNode> stack_p = new Stack <> ();       
    Stack<TreeNode> stack_q = new Stack <> ();
    if (p != null) stack_p.push( p ) ;
    if (q != null) stack_q.push( q ) ;
    while (!stack_p.isEmpty() && !stack_q.isEmpty()) {
        TreeNode pn = stack_p.pop() ;
        TreeNode qn = stack_q.pop() ;	    	
        if (pn.val != qn.val) return false ;
        if (pn.right != null) stack_p.push(pn.right) ;
        if (qn.right != null) stack_q.push(qn.right) ;
        if (stack_p.size() != stack_q.size()) return false ;
        if (pn.left != null) stack_p.push(pn.left) ;	    	 	    	 
        if (qn.left != null) stack_q.push(qn.left) ;
        if (stack_p.size() != stack_q.size()) return false ;
    }		     
    return stack_p.size() == stack_q.size() ;	 
}

对称树

Leetcode - 101 Symmetric Tree (Easy)

题目描述:判断一个二叉树是不是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

解法一:递归

public boolean isSymmetric(TreeNode root) {
    return isMirror(root, root);
}

public boolean isMirror(TreeNode t1, TreeNode t2) {
    if (t1 == null && t2 == null) return true;
    if (t1 == null || t2 == null) return false;
    return (t1.val == t2.val)
        && isMirror(t1.right, t2.left)
        && isMirror(t1.left, t2.right);
}

解法二:遍历

public boolean isSymmetric(TreeNode root) {
    Queue<TreeNode> q = new LinkedList<>();
    q.add(root);
    q.add(root);
    while (!q.isEmpty()) {
        TreeNode t1 = q.poll();
        TreeNode t2 = q.poll();
        if (t1 == null && t2 == null) continue;
        if (t1 == null || t2 == null) return false;
        if (t1.val != t2.val) return false;
        q.add(t1.left);
        q.add(t2.right);
        q.add(t1.right);
        q.add(t2.left);
    }
    return true;
}

反转二叉树

Leetcode - 226 Invert Binary Tree (Easy)

Input:
     4
   /   \
  2     7
 / \   / \
1   3 6   9
Output:
     4
   /   \
  7     2
 / \   / \
9   6 3   1

解法一:递归

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

解法二:非递归

public TreeNode invertTree(TreeNode root) {
    if (root == null) return null;
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode current = queue.poll();
        TreeNode temp = current.left;
        current.left = current.right;
        current.right = temp;
        if (current.left != null) queue.add(current.left);
        if (current.right != null) queue.add(current.right);
    }
    return root;
}

二叉树路径

Leetcode - 257 Binary Tree Paths (Easy)

Input:

   1
 /   \
2     3
 \
  5

Output: ["1->2->5", "1->3"]

Explanation: All root-to-leaf paths are: 1->2->5, 1->3

解法一:递归

public List<String> binaryTreePaths(TreeNode root) {
    List<String> answer = new ArrayList<String>();
    if (root != null) searchBT(root, "", answer);
    return answer;
}
private void searchBT(TreeNode root, String path, List<String> answer) {
    if (root.left == null && root.right == null) answer.add(path + root.val);
    if (root.left != null) searchBT(root.left, path + root.val + "->", answer);
    if (root.right != null) searchBT(root.right, path + root.val + "->", answer);
}

解法二:遍历

public List<String> binaryTreePaths(TreeNode root) {
    List<String> list=new ArrayList<String>();
    Queue<TreeNode> qNode=new LinkedList<TreeNode>();
    Queue<String> qStr=new LinkedList<String>();
    
    if (root==null) return list;
    qNode.add(root);
    qStr.add("");
    while(!qNode.isEmpty()) {
        TreeNode curNode=qNode.remove();
        String curStr=qStr.remove();
        
        if (curNode.left==null && curNode.right==null) list.add(curStr+curNode.val);
        if (curNode.left!=null) {
            qNode.add(curNode.left);
            qStr.add(curStr+curNode.val+"->");
        }
        if (curNode.right!=null) {
            qNode.add(curNode.right);
            qStr.add(curStr+curNode.val+"->");
        }
    }
    return list;
}

路径和 I

Leetcode - 112 Path Sum (Easy)

Given the below binary tree and sum = 22,

      5
     / \
    4   8
   /   / \
  11  13  4
 /  \      \
7    2      1

return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.

解法一:递归

public boolean hasPathSum(TreeNode root, int sum) {
    if(root == null) return false;
    if(root.left == null && root.right == null && sum == root.val) return true;
    return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}

解法二:遍历

public boolean hasPathSum(TreeNode root, int sum) {
    Stack <TreeNode> stack = new Stack<> ();	    
    stack.push(root) ;	    
    while (!stack.isEmpty() && root != null){
        TreeNode cur = stack.pop() ;	
        if (cur.left == null && cur.right == null){	    		
            if (cur.val == sum ) return true ;
        }
        if (cur.right != null) {
            cur.right.val = cur.val + cur.right.val ;
            stack.push(cur.right) ;
        }
        if (cur.left != null) {
            cur.left.val = cur.val + cur.left.val;
            stack.push(cur.left);
        }
    }	    
    return false ;
}

路径和 II

Leetcode - 113 Path Sum II (Medium)

题目描述:返回路径和为给定值的路径。

Given the below binary tree and sum = 22,

      5
     / \
    4   8
   /   / \
  11  13  4
 /  \    / \
7    2  5   1
Return:
[
   [5,4,11,2],
   [5,8,4,5]
]

解法一:递归

public List<List<Integer>> pathSum(TreeNode root, int sum) {
    List<List<Integer>> ansList = new ArrayList<>();
    pathSum(root, sum, new ArrayList<Integer>(), ansList);
    return ansList;
}

public void pathSum(TreeNode root, int sum, List<Integer> curList, List<List<Integer>> ansList){
    if(root == null) return;
    
    if(root.left == null && root.right == null && sum - root.val == 0){
        List<Integer> cur = new ArrayList(curList);
        cur.add(root.val);
        ansList.add(cur);
        return;
    }
    curList.add(root.val);
    pathSum(root.left, sum - root.val, curList, ansList);
    pathSum(root.right, sum - root.val, curList, ansList);
    curList.remove(curList.size() - 1);
    return;
}

解法二:遍历

public List<List<Integer>> pathSum(TreeNode root, int sum) {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<TreeNode>();
    int SUM = 0;
    TreeNode cur = root;
    TreeNode pre = null;
    while(cur!=null || !stack.isEmpty()){
        while(cur!=null){
            stack.push(cur);
            path.add(cur.val);
            SUM+=cur.val;
            cur=cur.left;
        }
        cur = stack.peek();
        if(cur.right!=null && cur.right!=pre){
            cur = cur.right;
            continue;
        } 
        if(cur.left==null && cur.right==null && SUM==sum) 
            res.add(new ArrayList<Integer>(path));

        pre = cur;
        stack.pop();
        path.remove(path.size()-1);
        SUM-=cur.val;
        cur = null;
    }
    return res;
}

根到叶节点数字之和

Leetcode - 129 Sum Root to Leaf Numbers (Medium)

Input: [4,9,0,5,1]
    4
   / \
  9   0
 / \
5   1
Output: 1026
Explanation:
The root-to-leaf path 4->9->5 represents the number 495.
The root-to-leaf path 4->9->1 represents the number 491.
The root-to-leaf path 4->0 represents the number 40.
Therefore, sum = 495 + 491 + 40 = 1026.
public int sumNumbers(TreeNode root) {
    return sum(root, 0); 
}
public int sum(TreeNode root, int s){
    if(root == null) return 0;
    if(root.left == null && root.right == null){
        return s * 10 + root.val;
    }
    return sum(root.left, s * 10 + root.val) + sum(root.right, s * 10 + root.val);
}

二叉树的最小深度

Leetcode - 111 Minimum Depth of Binary Tree (Easy)

Given binary tree [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
return its minimum depth = 2.
public int minDepth(TreeNode root) {
    if(root == null) return 0;
    int left = minDepth(root.left);
    int right = minDepth(root.right);
    return (left == 0 || right == 0) ? left + right + 1: Math.min(left,right) + 1;
}

后续遍历系列

二叉树的最大深度

Leetcode - 104 Maximum Depth of Binary Tree (Easy)

public int maxDepth(TreeNode root) {
    if(root == null) return 0;
    return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
}

判断平衡二叉树

Leetcode - 110 Balanced Binary Tree (Easy)

题目描述:平衡二叉树左右子树高度相差不能超过 1。

Given the following tree [3,9,20,null,null,15,7]:

    3
   / \
  9  20
    /  \
   15   7
Return true.
public boolean isBalanced(TreeNode root) {
    return depth(root) != -1;
}

public int depth(TreeNode root){
    if(root == null) return 0;
    int left = depth(root.left);
    int right = depth(root.right);
    
    if(left == -1 || right == -1){
        return -1;
    }
    
    if(Math.abs(left - right) > 1){
        return -1;
    }
    return Math.max(left, right) + 1;
}

二叉树最大路径和

Leetcode - 124 Binary Tree Maximum Path Sum (Hard)

题目描述:在二叉树中找到一个路径,使得路径和最大,不限制路径的起点与终点。

Input: [21,9,20,null,null,15,7]

   21
   / \
  9  20
    /  \
   15   7

Output: 56

题目描述:先递归到叶子节点,如果以 20 为根节点,最大路径和为 20 + 15 + 7,如果再向上回溯到 20 时,就不能同时取两条路径了,15 和 7 中只能选择一个最大的,记录所有路径中的最大路径和。

int maxValue;
public int maxPathSum(TreeNode root) {
    maxValue = Integer.MIN_VALUE;
    maxPathDown(root);
    return maxValue;
}
public int maxPathDown(TreeNode root){
    if(root == null) return 0;
    int left = Math.max(0, maxPathDown(root.left));
    int right = Math.max(0, maxPathDown(root.right));
    maxValue = Math.max(maxValue, left + right + root.val);
    return Math.max(left, right) + root.val;
}

打家劫舍 III

Leetcode - 337 House Robber III (Medium)

题目描述:连续偷窃相连的节点会触发警报,要求在不触发警报的情况下计算偷到的最大金额。

Input: [3,4,5,1,3,null,1]

     3
    / \
   4   5
  / \   \ 
 1   3   1

Output: 9
Explanation: Maximum amount of money the thief can rob = 4 + 5 = 9.
public int rob(TreeNode root) {
    int[] res = robSub(root);
    return Math.max(res[0], res[1]);
}
public int[] robSub(TreeNode root){
    if(root == null) return new int[2];
    
    int[] left = robSub(root.left);
    int[] right = robSub(root.right);
    
    int[] res = new int[2];
    // 不包含当前节点的最大值
    res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
    // 包含当前节点的最大值
    res[1] = root.val + left[0] + right[0];
    
    return res;
}

层次遍历系列

二叉树的层次遍历 II

Leetcode - 107 Binary Tree Level Order Traversal II (Easy)

题目描述:将二叉树从下到上存储。

Given binary tree [3,9,20,null,null,15,7],
    3
   / \
  9  20
    /  \
   15   7
return its bottom-up level order traversal as:
[
  [15,7],
  [9,20],
  [3]
]

解法一:递归 BFS

public List<List<Integer>> levelOrderBottom(TreeNode root) {
    List<List<Integer>> ansList = new ArrayList<>();
    backtrack(ansList, root, 0);
    return ansList;
}
public void backtrack(List<List<Integer>> ansList, TreeNode root, int level){
    if(root == null) return;
    if(ansList.size() <= level){
        ansList.add(0, new ArrayList<>());
    }
    backtrack(ansList, root.left, level + 1);
    backtrack(ansList, root.right, level + 1);
    ansList.get(ansList.size() - level - 1).add(root.val);
}

解法二:遍历

public List<List<Integer>> levelOrderBottom(TreeNode root) {
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    List<List<Integer>> wrapList = new LinkedList<List<Integer>>();
    
    if(root == null) return wrapList;
    
    queue.offer(root);
    while(!queue.isEmpty()){
        int levelNum = queue.size();
        List<Integer> subList = new LinkedList<Integer>();
        for(int i=0; i<levelNum; i++) {
            if(queue.peek().left != null) queue.offer(queue.peek().left);
            if(queue.peek().right != null) queue.offer(queue.peek().right);
            subList.add(queue.poll().val);
        }
        wrapList.add(0, subList);
    }
    return wrapList;
}

二叉树「之」字形层次遍历

Leetcode - 103 Binary Tree Zigzag Level Order Traversal (Medium)

Given binary tree [3,9,20,null,null,15,7],
    3
   / \
  9  20
    /  \
   15   7
return its zigzag level order traversal as:
[
  [3],
  [20,9],
  [15,7]
]

解法一:递归

public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    List<List<Integer>> ansList = new ArrayList<>();
    helper(ansList, root, 0);
    return ansList;
}

public void helper(List<List<Integer>> ansList, TreeNode root, int level){
    if(root == null) return;
    if(level >= ansList.size()){
        ansList.add(new ArrayList<Integer>());
    }
    if(level % 2 == 0){
        ansList.get(level).add(root.val);
    }else{
        ansList.get(level).add(0, root.val);
    }
    helper(ansList, root.left, level + 1);
    helper(ansList, root.right, level + 1);
}

解法二:遍历

public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    List<List<Integer>> ansList = new ArrayList<>();
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    if(root == null) return ansList;
    queue.offer(root);
    boolean isOdd = false;
    int size = 1;
    while(!queue.isEmpty()){
        List<Integer> curList = new ArrayList<>();
        for(int i = 0; i < size; i++){
            TreeNode curNode = queue.poll();
            if(!isOdd){
                curList.add(curNode.val);
            }else{
                curList.add(0, curNode.val);
            }
            if(curNode.left != null) queue.offer(curNode.left);
            if(curNode.right != null) queue.offer(curNode.right);
        }
        ansList.add(curList);
        isOdd = isOdd ? false : true;
        size = queue.size();
    }
    return ansList;
}

二叉树的右侧视图

Leetcode - 199 Binary Tree Right Side View (Medium)

题目描述:输出从二叉树右侧看到的节点,也就是每一层最右边的节点。

Input: [1,2,3,null,5,null,4]
Output: [1, 3, 4]
Explanation:

   1            <---
 /   \
2     3         <---
 \     \
  5     4       <---

解法一:递归

public List<Integer> rightSideView(TreeNode root) {
    List<Integer> result = new ArrayList<Integer>();
    rightView(root, result, 0);
    return result;
}
public void rightView(TreeNode curr, List<Integer> result, int currDepth){
    if(curr == null){
        return;
    }
    if(currDepth == result.size()){
        result.add(curr.val);
    }
    rightView(curr.right, result, currDepth + 1);
    rightView(curr.left, result, currDepth + 1);
}

解法二:遍历

public List<Integer> rightSideView(TreeNode root) {
    List<Integer> ansList = new ArrayList<Integer>();
    Queue<TreeNode> queue = new LinkedList<>();
    if(root == null) return ansList;
    queue.offer(root);
    int size = 1;
    while(!queue.isEmpty()){
        for(int i = 0; i < size; i++){
            TreeNode curNode = queue.poll();
            if(i == size - 1){
                ansList.add(curNode.val);
            }
            if(curNode.left != null) queue.offer(curNode.left);
            if(curNode.right != null) queue.offer(curNode.right);
        }
        size = queue.size();
    }
    return ansList;
}

二叉搜索树(BST)

验证二叉搜索树

Leetcode - 98 Validate Binary Search Tree (Medium)

题目描述:给出一个二叉树,判断是否是二叉搜索树。

Input:
    2
   / \
  1   3
Output: true

解题思路:二叉搜索树需要左子树比根节点小,右子树比根节点大,注意是子树,而不是结点,所以需要子树中的每一个结点都比根节点小/大。测试例会溢出,所以使用 Long 进行表示。

public boolean isValidBST(TreeNode root) {
    return backtrack(root, Long.MAX_VALUE, Long.MIN_VALUE);
}
public boolean backtrack(TreeNode root, long maxValue, long minValue){
    if(root == null) return true;
    if(root.val >= maxValue || root.val <= minValue) return false;
    return backtrack(root.left, root.val, minValue) && backtrack(root.right, maxValue, root.val);
}

二叉搜索树的最小公共父节点

Leetcode - 235 Lowest Common Ancestor of a Binary Search Tree (Easy)

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.

解题思路:利用好二叉搜索树的性质。

解法一:递归

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

解法二:遍历

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

二叉树的最小公共父节点

Leetcode - 236 Lowest Common Ancestor of a Binary Tree (Medium)

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null || root == p || root == q) return root;
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    return left == null ? right : right == null ? left : root;
}

有序数组转为二叉搜索树

Leetcode - 108 Convert Sorted Array to Binary Search Tree (Easy)

解题思路:二分搜索的思想,不明白为什么我用 0 到 num.length 划分,就只有 22%,而改成 0 到 num.length - 1 就是 100% 了。

public TreeNode sortedArrayToBST(int[] num) {
    return helper(num, 0, num.length - 1);
}
public TreeNode helper(int[] num, int low, int high) {
    if (low > high) return null;
    int mid = (low + high) / 2;
    TreeNode node = new TreeNode(num[mid]);
    node.left = helper(num, low, mid - 1);
    node.right = helper(num, mid + 1, high);
    return node;
}

有序链表转为二叉搜索树

Leetcode - 109 Convert Sorted List to Binary Search Tree

public TreeNode sortedListToBST(ListNode head) {
    if(head==null) return null;
    return toBST(head,null);
}
public TreeNode toBST(ListNode head, ListNode tail){
    ListNode slow = head;
    ListNode fast = head;
    if(head==tail) return null;

    while(fast!=tail&&fast.next!=tail){
        fast = fast.next.next;
        slow = slow.next;
    }
    TreeNode thead = new TreeNode(slow.val);
    thead.left = toBST(head,slow);
    thead.right = toBST(slow.next,tail);
    return thead;
}

二叉搜索树迭代器

Leetcode - 173 Binary Search Tree Iterator (Medium)

题目描述:构造一个迭代器,每次调用 next() 方法返回下一个最小节点的值,调用 hasNext() 返回是否有下一个最小节点。时间复杂度 O(1),空间复杂度 O(h),h 是树的高度。

class BSTIterator {
    Stack<TreeNode> stack = new Stack<TreeNode>();
    
    public BSTIterator(TreeNode root) {
        pushAll(root);
    }
    
    public int next() {
        TreeNode node = stack.pop();
        pushAll(node.right);
        return node.val;
    }
    
    public boolean hasNext() {
        return !stack.isEmpty();
    }
    
    public void pushAll(TreeNode node){
        for(;node != null;stack.push(node), node = node.left);
    }
}

二叉搜索树中第 K 小的节点

Leetcode - 230 Kth Smallest Element in a BST (Medium)

解法一:递归

int result = 0;
int count = 0;
public int kthSmallest(TreeNode root, int k) {
    traverse(root, k);
    return result;
}
public void traverse(TreeNode root, int k){
    if(root == null) return;
    traverse(root.left, k);
    if(++count == k) result = root.val;
    traverse(root.right, k);
}

解法二:遍历

public int kthSmallest(TreeNode root, int k) {
    Stack<TreeNode> stack = new Stack<TreeNode>();
    TreeNode cur = root;
    int count = 0;

    while(!stack.isEmpty() || cur != null){
        if(cur != null){
            stack.push(cur);
            cur = cur.left;
        }else{
            cur = stack.pop();
            if(++count == k){
                return cur.val;
            }
            cur = cur.right;
        }
    }
    return -1;
}

二叉树的序列化与反序列化

Leetcode - 297 Serialize and Deserialize Binary Tree (Hard)

题目描述:不限制方法。

private static final String spliter = ",";
private static final String NN = "X";
// 序列化
public String serialize(TreeNode root) {
    StringBuilder sb = new StringBuilder();
    buildString(root, sb);
    return sb.toString();
}

private void buildString(TreeNode node, StringBuilder sb) {
    if (node == null) {
        sb.append(NN).append(spliter);
    } else {
        sb.append(node.val).append(spliter);
        buildString(node.left, sb);
        buildString(node.right,sb);
    }
}
// 反序列化
public TreeNode deserialize(String data) {
    Deque<String> nodes = new LinkedList<>();
    nodes.addAll(Arrays.asList(data.split(spliter)));
    return buildTree(nodes);
}

private TreeNode buildTree(Deque<String> nodes) {
    String val = nodes.remove();
    if (val.equals(NN)) return null;
    else {
        TreeNode node = new TreeNode(Integer.valueOf(val));
        node.left = buildTree(nodes);
        node.right = buildTree(nodes);
        return node;
    }
}

恢复二叉搜索树

Leetcode - 99 Recover Binary Search Tree (Hard)

题目描述:二叉搜索树中有两个节点是错位的,在不改变树结构的条件下使二叉树复位。

解题思路:首先想到的就是中序遍历的方法,找到两个错位的节点,因为中序遍历之后前面的节点一定小于后面的节点,如果不是则表示错位了,但是题目进阶要求空间复杂度为 O(1),中序遍历不论是栈+遍历还是递归的方式空间复杂度都是 O(n),有没有一种二叉树的遍历方式空间复杂度为 O(1)?有,那就是 Morris Traversal。

解法一:递归

TreeNode firstNode = null;
    TreeNode secondNode = null;
    TreeNode preNode = new TreeNode(Integer.MIN_VALUE);
    public void recoverTree(TreeNode root) {
        traverse(root);
        int temp = firstNode.val;
        firstNode.val = secondNode.val;
        secondNode.val = temp;
    }
    public void traverse(TreeNode root){
        if(root == null) return;
        traverse(root.left);
        if(firstNode == null && preNode.val >= root.val){
            firstNode = preNode;  // 注意,第一个是 preNode
        }
        if(firstNode != null && preNode.val >= root.val){
            secondNode = root;    // 第二个是 root
        }
        preNode = root;
        traverse(root.right);
    }

解法二:Morris Traversal

利用所有叶子节点的 right 指针,指向后继节点,组成一个环,这样就能通过指针的方式记录下当前根节点的位置。

public void recoverTree(TreeNode root) {
    TreeNode first = null;
    TreeNode second = null;

    TreeNode pred = null; // 左子树的最右边节点
    TreeNode prev = null; 

    TreeNode curr = root;

    while(curr != null){
        //for each node, we compare it with prev node as we did in in-order-traversal
        if(prev != null && curr.val <= prev.val){
            if(first == null) first = prev;
            second = curr;
        }

        if(curr.left != null){
            // 找到左子树的最右边的节点
            pred = curr.left;
            while(pred.right != null && pred.right != curr){
                pred = pred.right;
            }

            if(pred.right == curr){
                // 第二次访问该节点,返回到当前子树的后继节点
                pred.right = null;
                prev = curr;
                curr = curr.right;
            }else{
                pred.right = curr;
                curr = curr.left;
            }

        }else{
            // 没有左子树,直接访问右子树
            prev = curr;
            curr = curr.right;
        }
    }

    int temp = first.val;
    first.val = second.val;
    second.val = temp;
}

字典树(Trie)

实现字典树(Trie)

Leetcode - 207 Implement Trie(Prefix Tree) (Medium)

class TrieNode{
    public char val;
    public boolean isWord;
    public TrieNode[] children = new TrieNode[26];
    public TrieNode(){}
    TrieNode(char c){
        TrieNode node = new TrieNode();
        node.val = c;
    }
}

class Trie {

    private TrieNode root;
    public Trie() {
        root = new TrieNode();
        root.val = ' ';
    }
    
    // 插入
    public void insert(String word) {
        TrieNode ws = root;
        for(int i = 0; i < word.length(); i++){
            char c = word.charAt(i);
            if(ws.children[c - 'a'] == null){
                ws.children[c - 'a'] = new TrieNode(c);
            }
            ws = ws.children[c - 'a'];
        }
        ws.isWord = true;
    }
    
    // 搜索
    public boolean search(String word) {
        TrieNode ws = root;
        for(int i = 0; i < word.length(); i++){
            char c = word.charAt(i);
            if(ws.children[c - 'a'] == null) return false;
            ws = ws.children[c - 'a'];
        }
        return ws.isWord;
    }
    
    // 判断是否包含前缀字符
    public boolean startsWith(String prefix) {
        TrieNode ws = root; 
        for(int i = 0; i < prefix.length(); i++){
            char c = prefix.charAt(i);
            if(ws.children[c - 'a'] == null) return false;
            ws = ws.children[c - 'a'];
        }
        return true;
    }
}

单词搜索 II

Leetcode - 212 Word Search II (Hard)

题目描述:搜索在字符矩阵中的字符串。

Input: 
board = [
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]
words = ["oath","pea","eat","rain"]

Output: ["eat","oath"]

解题思路:先将带查找字符串构建成字典树,再利用回溯法判断矩阵中的字符串是否在字典树中。

public List<String> findWords(char[][] board, String[] words) {
    List<String> res = new ArrayList<>();
    TrieNode root = buildTrie(words);
    for(int i = 0; i < board.length; i++){
        for(int j = 0; j < board[0].length; j++){
            dfs(board, i, j, root, res);
        }
    }
    return res;
}

public void dfs(char[][] board, int i, int j, TrieNode p, List<String> res){
    char c = board[i][j];
    if(c == '#' || p.next[c - 'a'] == null) return;
    p = p.next[c - 'a'];
    if(p.word != null){
        res.add(p.word);
        p.word = null; // 去重
    }
    
    board[i][j] = '#';
    if (i > 0) dfs(board, i - 1, j, p, res); 
    if (j > 0) dfs(board, i, j - 1, p, res);
    if (i < board.length - 1) dfs(board, i + 1, j, p, res); 
    if (j < board[0].length - 1) dfs(board, i, j + 1, p, res); 
    board[i][j] = c;
}

public TrieNode buildTrie(String[] words){
    TrieNode root = new TrieNode();
    for(String s : words){
        TrieNode p = root;
        for(char c : s.toCharArray()){
            int i = c - 'a';
            if(p.next[i] == null) p.next[i] = new TrieNode();
            p = p.next[i];
        }
        p.word = s;
    }
    return root;
}

class TrieNode{
    TrieNode[] next = new TrieNode[26];
    String word;
}

其它

每个节点的右向指针

Leetcode - 116 Populating Next Right Pointers in Each Node (Medium)

题目描述:假设二叉树是满二叉树。


解题思路:这道题出的非常好,从上一层对下一层修改。

public Node connect(Node root) {
    Node levelStart = root;
    while(levelStart != null){
        Node cur = levelStart;
        while(cur != null){
            if(cur.left != null) cur.left.next = cur.right;
            if(cur.right != null && cur.next != null) cur.right.next = cur.next.left;
            cur = cur.next;
        }
        levelStart = levelStart.left;
    }
    return root;
}

每个节点的右向指针

Leetcode - 117 Populating Next Right Pointers in Each Node II (Medium)

题目描述:与上一道题不同的是,本次没有规定是满二叉树。

public Node connect(Node root) {
    Node cur = root;
    while (cur != null) {
        Node dummy = new Node(0);
        Node son = dummy;
        while (cur != null) {
            if (cur.left != null) {
                son.next = cur.left;
                son = son.next;
            }
            if (cur.right != null) {
                son.next = cur.right;
                son = son.next;
            }
            cur = cur.next;
        }
        cur = dummy.next;
    }
    return root;
}

根据前序和中序遍历构建二叉树

Leetcode - 105 Construct Binary Tree from Preorder and Inorder Traversal (Medium)

public TreeNode buildTree(int[] preorder, int[] inorder) {
    return backtrack(preorder, inorder, 0, 0, inorder.length - 1);
}
public TreeNode backtrack(int[] preorder, int[] inorder, int preStart, int inStart, int inEnd{
    if (preStart > preorder.length - 1 || inStart > inEnd) {
        return null;
    }
    TreeNode root = new TreeNode(preorder[preStart]);
    int index = 0;
    for(int i = inStart; i <= inEnd; i++){
        if(inorder[i] == preorder[preStart]){
            index = i;
        }
    }
    root.left = backtrack(preorder, inorder, preStart + 1, inStart, index - 1);
    root.right = backtrack(preorder, inorder, preStart + index - inStart + 1, index + 1, inEnd);
    return root;
}

将二叉树展开成链表

Leetcode - 114 Flatten Binary Tree to Linked List (Medium)

Input:
    1
   / \
  2   5
 / \   \
3   4   6
Output:
1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

解法一:递归。

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

解法二:遍历。

public void flatten(TreeNode root) {
    while(root != null){
        if(root.left != null){
            TreeNode prev = root.left;
            while(prev.right != null){
                prev = prev.right;
            }
            prev.right = root.right;
            root.right = root.left;
            root.left = null;
        }
        root = root.right;
    }
}

二叉树路径和 Ⅲ

Leetcode - 437 Path Sum III (Easy)

题目描述:找出二叉树中路径和为 sum 的数量,开始结点不需要一定是根节点或叶子节点,但是必须是向下遍历。

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1

Return 3. The paths that sum to 8 are:

1.  5 -> 3
2.  5 -> 2 -> 1
3. -3 -> 11
public int pathSum(TreeNode root, int sum) {
    if(root == null) return 0;
    return pathSumFrom(root, sum) + pathSum(root.left, sum)
        + pathSum(root.right, sum);
}

public int pathSumFrom(TreeNode root, int sum){
    if(root == null) return 0;
    return (root.val == sum ? 1 : 0) + pathSumFrom(root.left, sum - root.val) 
        + pathSumFrom(root.right, sum - root.val);
}

合并两棵二叉树

Leetcode - 617 Merge Two Binary Trees (Easy)

Input: 
	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
Output: 
Merged tree:
	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
    if(t1 == null && t2 == null) return null;
    int val = (t1 == null ? 0 : t1.val) + (t2 == null ? 0 : t2.val);
    TreeNode root = new TreeNode(val);
    root.left = mergeTrees(t1 != null ? t1.left : null, t2 != null ? t2.left : null);
    root.right = mergeTrees(t1 != null ? t1.right : null, t2 != null ? t2.right : null);
    return root;
}