递归

一棵树要么是空树,要么有两个指针,每个指针指向一棵树。树是一种递归结构,很多树的问题可以使用递归来处理。

Leetcode-104. 二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:

给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回它的最大深度 3 。

解法:

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

Leetcode-110. 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

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

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7
返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4
返回 false 。

解法:

  • Java
/** * 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 (root==null) return true;
        int l = treeDepth(root.left);
        int r = treeDepth(root.right);
        return Math.abs(l-r)<=1 && isBalanced(root.left) && isBalanced(root.right);
    }
    private int treeDepth(TreeNode node) {
   
        if (node==null) return 0;
        return 1+Math.max(treeDepth(node.left), treeDepth(node.right));
    }
}

注意基本类型传入后是不会被改变的,

Leetcode-543. 二叉树的直径

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过根结点。

示例 :

给定二叉树

          1
         / \
        2   3
       / \     
      4   5    
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
  • 注意:两结点之间的路径长度是以它们之间边的数目表示。

解法:

  • Java

dfs
按照常用方法计算一个节点的深度:max(depth of node.left, depth of node.right) + 1。在计算的同时,经过这个节点的路径长度为 1 + (depth of node.left) + (depth of node.right) 。搜索每个节点并记录这些路径经过的点数最大值,期望长度是结果 - 1。

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

Leetcode-226. 翻转二叉树

翻转一棵二叉树。

示例:

输入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9
输出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

备注:
这个问题是受到 Max Howell 的 原问题 启发的 :

谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。

解法:

  • Java
    为什么Max Howell做不出来。。。
class Solution {
   
    public TreeNode invertTree(TreeNode root) {
   
        if (root==null) return null;
        TreeNode tmp = root.left;
        root.left = invertTree(root.right);
        root.right = invertTree(tmp);
        return root;
    }
}

Leetcode-617. 合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:

输入: 
	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
输出: 
合并后的树:
	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7

注意: 合并必须从两个树的根节点开始。

解法:

  • Java
class Solution {
   
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
   
        if (t1==null) return t2;
        if (t2==null) return t1;
        TreeNode node = new TreeNode(t1.val+t2.val);
        node.left = mergeTrees(t1.left, t2.left);
        node.right = mergeTrees(t1.right, t2.right);
        return node;
    }
}

Leetcode-112. 路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

说明: 叶子节点是指没有子节点的节点。

示例:

给定如下二叉树,以及目标和 sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。

解法:

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

Leetcode-437. 路径总和 III

给定一个二叉树,它的每个结点都存放着一个整数值。

找出路径和等于给定数值的路径总数。

路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。

示例:

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

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

返回 3。和等于 8 的路径有:

1.  5 -> 3
2.  5 -> 2 -> 1
3.  -3 -> 11

解法:

  • Java
class Solution {
   
    public int pathSum(TreeNode root, int sum) {
   
        if (root==null) return 0;
        return pathSumStartWithRoot(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
    }
    private int pathSumStartWithRoot(TreeNode node, int sum) {
   
        if (node == null) return 0;
        int res = 0;
        if (node.val == sum) res++;
        res += pathSumStartWithRoot(node.left, sum-node.val) + pathSumStartWithRoot(node.right, sum-node.val);
        return res;
    }
}

Leetcode-572. 另一个树的子树

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

示例 1:

给定的树 s:

     3
    / \
   4   5
  / \
 1   2
给定的树 t:

   4 
  / \
 1   2
返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。

示例 2:

给定的树 s:

     3
    / \
   4   5
  / \
 1   2
    /
   0
给定的树 t:

   4
  / \
 1   2
返回 false。

解法:

  • Java
class Solution {
   
    public boolean isSubtree(TreeNode s, TreeNode t) {
   
        if (s==null) return false;
        return isSubtreeWithRoot(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t);
    }
    private boolean isSubtreeWithRoot(TreeNode s, TreeNode t) {
   
        if (s==null && t==null) return true;
        if (s==null || t==null) return false;
        if (s.val != t.val) return false;
        return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right);
    }
}

Leetcode-101. 对称二叉树

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

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

说明:

如果你可以运用递归和迭代两种方法解决这个问题,会很加分。

解法:

  • java
class Solution {
   
    public boolean isSymmetric(TreeNode root) {
   
        if (root==null) return true;
        return TwoSymmetricTrees(root.left, root.right);
    }
    private boolean TwoSymmetricTrees(TreeNode root1, TreeNode root2) {
   
        if (root1==null && root2==null) return true;
        if (root1==null || root2==null) return false;
        if (root1.val != root2.val) return false;
        return TwoSymmetricTrees(root1.left, root2.right) && TwoSymmetricTrees(root1.right, root2.left);
    }
}

Leetcode-111. 二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

示例:

给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回它的最小深度  2.

解法:

  • Java
class Solution {
   
    public int minDepth(TreeNode root) {
   
        if (root == null) return 0;
        int left = minDepth(root.left);
        int right = minDepth(root.right);
        if (left == 0 || right == 0) return left + right + 1;
        return Math.min(left, right) + 1;
    }
}

Leetcode-404. 左叶子之和

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

示例:

3

/
9 20
/
15 7

在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

解法:

  • Java
class Solution {
   
    public int sumOfLeftLeaves(TreeNode root) {
   
        if (root==null) return 0;
        if (isLeaf(root.left)) return root.left.val + sumOfLeftLeaves(root.right);
        return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
    }
    private boolean isLeaf(TreeNode node) {
   
        if (node == null) return false;
        return node.left==null && node.right==null;
    }
}

Leetcode-687. 最长同值路径

给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。

注意:两个节点之间的路径长度由它们之间的边数表示。

示例 1:

输入:

              5
             / \
            4   5
           / \   \
          1   1   5
输出:

2

示例 2:

输入:

              1
             / \
            4   5
           / \   \
          4   4   5
输出:

2

注意: 给定的二叉树不超过10000个结点。 树的高度不超过1000。

解法:

  • java
class Solution {
   
    private int path;
    public int longestUnivaluePath(TreeNode root) {
   
        if (root==null) return 0;
        dfs(root);
        return path;
    }
    private int dfs(TreeNode root) {
   
        if (root==null) return 0;
        int left = dfs(root.left);
        int right = dfs(root.right);
        int leftPath = (root.left!=null && root.val==root.left.val)? left+1:0;
        int rightPath = (root.right!=null && root.val==root.right.val)? right+1:0;
        path = Math.max(path, leftPath+rightPath);
        return Math.max(leftPath, rightPath);
    }
}

Leetcode-337. 打家劫舍 III

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例 1:

输入: [3,2,3,null,3,null,1]

     3
    / \
   2   3
    \   \ 
     3   1

输出: 7 
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

示例 2:

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

     3
    / \
   4   5
  / \   \ 
 1   3   1

输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

解法:

  • Java
public int rob(TreeNode root) {
   
    if (root == null) return 0;
    int val1 = root.val;
    if (root.left != null) val1 += rob(root.left.left) + rob(root.left.right);
    if (root.right != null) val1 += rob(root.right.left) + rob(root.right.right);
    int val2 = rob(root.left) + rob(root.right);
    return Math.max(val1, val2);
}

Leetcode-671. 二叉树中第二小的节点

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值。

给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。

示例 1:

输入: 
    2
   / \
  2   5
     / \
    5   7

输出: 5
说明: 最小的值是 2 ,第二小的值是 5 。

示例 2:

输入: 
    2
   / \
  2   2

输出: -1
说明: 最小的值是 2, 但是不存在第二小的值。

解法:

  • Java

根节点一定是最小的
如果左边值等于root值,去找左边第二小的,同理右边

class Solution {
   
    public int findSecondMinimumValue(TreeNode root) {
   
        if (root==null || (root.left==null&&root.right==null)) return -1;
        int l = root.left.val;
        int r = root.right.val;
        if (l==root.val) l = findSecondMinimumValue(root.left);
        if (r==root.val) r = findSecondMinimumValue(root.right);
        if (l==-1 && r==-1) return -1;
        if (l!=-1 && r!=-1) return Math.min(l, r);
        return l==-1? r: l;
    }
}

层次遍历

使用 BFS 进行层次遍历。不需要使用两个队列来分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。

Leetcode-637. 二叉树的层平均值

给定一个非空二叉树, 返回一个由每层节点平均值组成的数组.

示例 1:

输入:
    3
   / \
  9  20
    /  \
   15   7
输出: [3, 14.5, 11]
解释:
第0层的平均值是 3,  第1层是 14.5, 第2层是 11. 因此返回 [3, 14.5, 11].

解法:

  • Java
    BFS
class Solution {
   
    public List<Double> averageOfLevels(TreeNode root) {
   
        Queue<TreeNode> queue = new LinkedList<>();
        List<Double> res = new ArrayList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
   
            int n = queue.size();
            double sum = 0;
            for (int i=0;i<n;i++) {
   
                TreeNode node = queue.remove();
                sum += node.val;
                if (node.left!=null) queue.add(node.left);
                if (node.right!=null) queue.add(node.right);
            }
            res.add(sum/n);
        }
        return res;
    }
}

Leetcode-513. 找树左下角的值

给定一个二叉树,在树的最后一行找到最左边的值。

示例 1:

输入:

    2
   / \
  1   3

输出:
1

示例 2:

输入:

        1
       / \
      2   3
     /   / \
    4   5   6
       /
      7

输出:
7

注意: 您可以假设树(即给定的根节点)不为 NULL。

解法:

  • Java
    记录最深一层的第一个node
class Solution {
   
    public int findBottomLeftValue(TreeNode root) {
   
        Queue<TreeNode> q = new LinkedList<>();
        TreeNode res = null;
        q.add(root);
        while(!q.isEmpty()) {
   
            int n = q.size();
            for (int i=0;i<n;i++) {
   
                TreeNode node = q.remove();
                if (i==0) res = node;
                if (node.left!=null) q.add(node.left);
                if (node.right!=null) q.add(node.right);
            }
        }
        return res.val;
    }
}

前中后序遍历

    1
   / \
  2   3
 / \   \
4   5   6
  • 层次遍历顺序:[1 2 3 4 5 6]
  • 前序遍历顺序:[1 2 4 5 3 6]
  • 中序遍历顺序:[4 2 5 1 3 6]
  • 后序遍历顺序:[4 5 2 6 3 1]
  • 层次遍历使用 BFS 实现,利用的就是 BFS 一层一层遍历的特性;而前序、中序、后序遍历利用了 DFS 实现。

前序、中序、后序遍只是在对节点访问的顺序有一点不同,其它都相同。

① 前序

void dfs(TreeNode root) {
visit(root);
dfs(root.left);
dfs(root.right);
}
② 中序

void dfs(TreeNode root) {
dfs(root.left);
visit(root);
dfs(root.right);
}
③ 后序

void dfs(TreeNode root) {
dfs(root.left);
dfs(root.right);
visit(root);
}

Leetcode-144. 二叉树的前序遍历

给定一个二叉树,返回它的 前序 遍历。

示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [1,2,3]
  • 进阶: 递归算法很简单,你可以通过迭代算法完成吗?

解法:

  • Java

递归

class Solution {
   
    public List<Integer> preorderTraversal(TreeNode root) {
   
        List<Integer> lst = new ArrayList<>();
        dfs(root, lst);
        return lst;
    }
    public void dfs(TreeNode root, List<Integer> lst) {
   
        if (root==null) return;
        lst.add(root.val);
        dfs(root.left, lst);
        dfs(root.right, lst);
    }
}

非递归
注意非递归使用的是dfs,需要用栈来存储TreeNode,与层次遍历中的queue要区分开
同时为了保持root-> left -> right的顺序,需要将right先入栈

/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */
class Solution {
   
    public List<Integer> preorderTraversal(TreeNode root) {
   
        List<Integer> res = new ArrayList<>();
        if (root==null) return res;
        Deque<TreeNode> stack = new LinkedList<>();
        stack.push(root);
        while (! stack.isEmpty()) {
   
            TreeNode node = stack.pop();
            res.add(node.val);
            if (node.right!=null) stack.push(node.right);
            if (node.left!=null) stack.push(node.left);
        }
        return res;
    }
}

Leetcode-145. 二叉树的后序遍历

给定一个二叉树,返回它的 后序 遍历。

示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [3,2,1]
  • 进阶: 递归算法很简单,你可以通过迭代算法完成吗?

解法:

  • Java

递归

class Solution {
   
    public List<Integer> postorderTraversal(TreeNode root) {
   
        List<Integer> lst = new ArrayList<>();
        dfs(root, lst);
        return lst;
    }
    public void dfs(TreeNode root, List<Integer> lst) {
   
        if (root==null) return;
        dfs(root.left, lst);
        dfs(root.right, lst);
        lst.add(root.val);
    }
}
  • Java

非递归
前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。

class Solution {
   
    public List<Integer> postorderTraversal(TreeNode root) {
   
        Deque<TreeNode> stack = new LinkedList<>();
        List<Integer> lst = new ArrayList<>();
        stack.push(root);
        while (!stack.isEmpty()) {
   
            TreeNode node = stack.pop();
            if (node==null) continue;
            lst.add(node.val);
            if (node.left!=null) stack.push(node.left);
            if (node.right!=null) stack.push(node.right);
        }
        Collections.reverse(lst);
        return lst;
    }
}

Leetcode-94. 二叉树的中序遍历

给定一个二叉树,返回它的中序 遍历。

示例:

输入: [1,null,2,3]
   1
    \
     2
    /
   3

输出: [1,3,2]
  • 进阶: 递归算法很简单,你可以通过迭代算法完成吗?

解法:

  • Java

递归

class Solution {
   
    public List<Integer> inorderTraversal(TreeNode root) {
   
        List<Integer> lst = new ArrayList<>();
        this.dfs(root, lst);
        return lst;
    }
    public void dfs(TreeNode root, List<Integer> lst) {
   
        if (root==null) return ;
        dfs(root.left, lst);
        lst.add(root.val);
        dfs(root.right, lst);
    }
}
  • Java

非递归

/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */
class Solution {
   
    public List<Integer> inorderTraversal(TreeNode root) {
   
        List<Integer> res = new ArrayList<>();
        if (root==null) return res;
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        // stack.push(cur); 这里不要提前push进去root
        while (!stack.isEmpty() || cur!=null) {
   
            while (cur != null) {
   
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode node = stack.pop();
            res.add(node.val);
            cur = node.right;
        }
        return res;
    }
}

BST

二叉查找树(BST):根节点大于等于左子树所有节点,小于等于右子树所有节点。

二叉查找树中序遍历有序。

Leetcode- 669. 修剪二叉搜索树

给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。

示例 1:

输入: 
    1
   / \
  0   2

  L = 1
  R = 2

输出: 
    1
      \
       2

示例 2:

输入: 
    3
   / \
  0   4
   \
    2
   /
  1

  L = 1
  R = 3

输出: 
      3
     / 
   2   
  /
 1

解法:

  • Java

当 node.val > R,那么修剪后的二叉树必定出现在节点的左边。

类似地,当 node.val < L,那么修剪后的二叉树出现在节点的右边。

否则,我们将会修剪树的两边。

class Solution {
   
    public TreeNode trimBST(TreeNode root, int L, int R) {
   
        if (root==null) return null;
        if (root.val > R) return trimBST(root.left, L, R);
        if (root.val < L) return trimBST(root.right, L, R);
        root.left = trimBST(root.left, L, R);
        root.right = trimBST(root.right, L, R);
        return root;
    }
}

Leetcode-230. 二叉搜索树中第K小的元素

给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。

说明:
你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。

示例 1:

输入: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
输出: 1

示例 2:

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

进阶:

  • 如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化 kthSmallest 函数?

解法:

  • Java
    中序遍历得到第k小的元素
class Solution {
   
    private int val = 0;
    private int cnt = 0;        
    public int kthSmallest(TreeNode root, int k) {
   
        dfs(root, k);
        return val;        
    }
    private void dfs(TreeNode node, int k) {
   
        if (node == null) return;
        dfs(node.left, k);
        cnt++;
        if (cnt==k) {
   
            val = node.val;
            return;
        }
        dfs(node.right, k);
    }
}

递归取数

class Solution {
   
    public int kthSmallest(TreeNode root, int k) {
   
        int leftCnt = count(root.left);
        if (leftCnt==k-1) return root.val;
        if (leftCnt<k-1) return kthSmallest(root.right, k-leftCnt-1);
        return kthSmallest(root.left, k);
    }
    private int count(TreeNode node) {
   
        if (node==null) return 0;
        return 1 + count(node.left) + count(node.right);
    }
}

Leetcode-538. 把二叉搜索树转换为累加树

给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。

例如:

输入: 二叉搜索树:
              5
            /   \
           2     13

输出: 转换为累加树:
             18
            /   \
          20     13

解法:

  • Java

在递归方法中,我们维护一些递归调用过程中可以访问和修改的全局变量。首先我们判断当前访问的节点是否存在,如果存在就递归右子树,递归回来的时候更新总和和当前点的值,然后递归左子树。如果我们分别正确地递归 root.right 和 root.left ,那么我们就能正确地用大于某个节点的值去更新此节点,然后才遍历比它小的值。

class Solution {
   
    private int sum = 0;
    public TreeNode convertBST(TreeNode root) {
   
        dfs(root);
        return root;
    }
    private void dfs(TreeNode node) {
   
        if (node==null) return;
        dfs(node.right);
        sum += node.val;
        node.val = sum;
        dfs(node.left);
    }
}

Leetcode-235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

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

例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。

示例 2:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。

说明:

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉搜索树中。

解法:

  • Java
  1. 从根节点开始遍历树
  2. 如果节点 pp 和节点 qq 都在右子树上,那么以右孩子为根节点继续 1 的操作
  3. 如果节点 pp 和节点 qq 都在左子树上,那么以左孩子为根节点继续 1 的操作
  4. 如果条件 2 和条件 3 都不成立,这就意味着我们已经找到节 pp 和节点 qq 的 LCA 了
class Solution {
   
    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;
    }
}

Leetcode-236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

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

例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明:

所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。

解法:

  • Java
class Solution {
   
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
   
        if (root==null || root==p || root==q) return root;
        TreeNode l = lowestCommonAncestor(root.left, p, q);
        TreeNode r = lowestCommonAncestor(root.right, p, q);
        if (l!=null && r!=null) return root;
        return l==null? r: l;
    }
}

Leetcode-108. 将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

解法:

  • Java

不要再将mid元素分到left或者right中

class Solution {
   
    public TreeNode sortedArrayToBST(int[] nums) {
   
        return dfs(nums, 0, nums.length-1);    
    }
    private TreeNode dfs(int[] nums, int start, int end) {
   
        if (start>end) return null;
        int mid = start + (end-start)/2;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = dfs(nums, start, mid-1);
        root.right = dfs(nums, mid+1, end);
        return root;
    }
}

Leetcode- 109. 有序链表转换二叉搜索树

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

解法:

  • java

关键点同样是找中间的节点

class Solution {
   
    public TreeNode sortedListToBST(ListNode head) {
   
        if (head==null) return null;
        if (head.next==null) return new TreeNode(head.val);
        ListNode preMid = getPreMid(head);
        ListNode mid = preMid.next;
        preMid.next = null;
        TreeNode root = new TreeNode(mid.val);
        root.left = sortedListToBST(head);
        root.right = sortedListToBST(mid.next);
        return root;
    }
    private ListNode getPreMid(ListNode head) {
   
        if (head==null) return null;
        ListNode pre = head, slow = head, fast = head;
        while (fast!=null && fast.next!=null) {
   
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        return pre;
    }
}

Leetcode-653. 两数之和 IV - 输入 BST

给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。

案例 1:

输入:
5
/
3 6
/ \
2 4 7

Target = 9

输出: True

案例 2:

输入:
5
/
3 6
/ \
2 4 7

Target = 28

输出: False

解法:

  • Java

先使用中序遍历将BST顺序存储,然后用双指针进行遍历。
注意因为需要两个数相加等于k,所以l不可以等于h,指针不能够重叠

class Solution {
   
    public boolean findTarget(TreeNode root, int k) {
   
        if (root==null) return false;
        List<Integer> lst = new ArrayList<>();
        inOrder(root, lst);
        int l = 0, h = lst.size()-1;
        while (l<h) {
   
            int sum = lst.get(l) + lst.get(h);
            if (sum==k) return true;
            else if (sum>k) h--;
            else l++;
        }
        return false;
    }
    private void inOrder(TreeNode root, List<Integer> lst) {
   
        if (root==null) return;
        inOrder(root.left, lst);
        lst.add(root.val);
        inOrder(root.right, lst);
    }
}

Leetcode-530. 二叉搜索树的最小绝对差

给定一个所有节点为非负值的二叉搜索树,求树中任意两节点的差的绝对值的最小值。

示例 :

输入:

   1
    \
     3
    /
   2

输出:
1

解释:
最小绝对差为1,其中 2 和 1 的差的绝对值为 1(或者 2 和 3)。
  • 注意: 树中至少有2个节点。

解法:

  • Java

利用二叉查找树的中序遍历为有序的性质,计算中序遍历中临近的两个节点之差的绝对值,取最小值。

class Solution {
   
    private int minDiff = Integer.MAX_VALUE;
    private TreeNode preNode = null;
    public int getMinimumDifference(TreeNode root) {
   
        inOrder(root);
        return minDiff;
    }
    private void inOrder(TreeNode node) {
   
        if (node==null) return;
        inOrder(node.left);
        if (preNode != null) minDiff = Math.min(minDiff, node.val-preNode.val);
        preNode = node;
        inOrder(node.right);
    }
}

Leetcode-501. 二叉搜索树中的众数

给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。

假定 BST 有如下定义:

  • 结点左子树中所含结点的值小于等于当前结点的值
  • 结点右子树中所含结点的值大于等于当前结点的值
  • 左子树和右子树都是二叉搜索树

例如:

给定 BST [1,null,2,2],

   1
    \
     2
    /
   2
返回[2].

提示:如果众数超过1个,不需考虑输出顺序

进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)

解法:

  • Java

依然是使用中序遍历,中序遍历可以解决大部分的BST问题

class Solution {
   
    private int maxCnt = 0;
    private int curCnt = 0;
    private TreeNode preNode = null;
    public int[] findMode(TreeNode root) {
   
        List<Integer> lst = new ArrayList<>();
        inOrder(root, lst);
        int idx = 0;
        int[] ans = new int[lst.size()];
        for (int num :lst) {
   
            ans[idx++] = num;
        }
        return ans;
    }
    
    private void inOrder(TreeNode node, List<Integer> lst) {
   
        if (node==null) return ;
        inOrder(node.left, lst);
        if (preNode != null) {
   
            if (preNode.val == node.val) curCnt++;
            else curCnt = 1;
        } else {
   
            curCnt = 1;
        }
        if (curCnt > maxCnt) {
   
            maxCnt = curCnt;
            lst.clear();
            lst.add(node.val);
        } else if (curCnt==maxCnt) {
   
            lst.add(node.val);
        }
        preNode = node;
        inOrder(node.right, lst);
    }
}

Trie

Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。

Leetcode-208. 实现 Trie (前缀树)

实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。

示例:

Trie trie = new Trie();

trie.insert("apple");
trie.search("apple");   // 返回 true
trie.search("app");     // 返回 false
trie.startsWith("app"); // 返回 true
trie.insert("app");   
trie.search("app");     // 返回 true

说明:

  • 你可以假设所有的输入都是由小写字母 a-z 构成的。
  • 保证所有输入均为非空字符串。

解法:

  • Java
class Trie {
   
    
    class TrieNode {
   
        public char val;
        public TrieNode[] children = new TrieNode[26];
        public boolean isWord;
        public TrieNode(char c) {
   
            val = c;
            isWord = false;
        }
    }
    
    public TrieNode root = new TrieNode(' ');
    
    public void insert(String word) {
   
        TrieNode node = root;
        for (int i=0;i<word.length();i++) {
   
            char c = word.charAt(i);
            if (node.children[c-'a']==null) node.children[c-'a'] = new TrieNode(c);
            node = node.children[c-'a'];
        }
        node.isWord = true;
    }
    
    public boolean search(String word) {
   
        TrieNode node = root;
        for (int i=0;i<word.length();i++) {
   
            char c = word.charAt(i);
            if (node.children[c-'a']==null) return false;
            node = node.children[c-'a'];
        }
        return node.isWord;
    }
    
    public boolean startsWith(String prefix) {
   
        TrieNode node = root;
        for (int i=0;i<prefix.length();i++) {
   
            char c = prefix.charAt(i);
            if (node.children[c-'a']==null) return false;
            node = node.children[c-'a'];
        }
        return true;
    }
}

另一种写法

class Trie {
   
    
    private class TrieNode {
   
        TrieNode[] children = new TrieNode[26];
        boolean isWord;
    }
    
    private TrieNode root = new TrieNode();
    
    public Trie() {
   }
    
    public void insert(String word) {
   
        insert(word, root);
    }
    
    private void insert(String word, TrieNode node) {
   
        if (node == null) return ;
        if (word.length()==0) {
   
            node.isWord = true;
            return;
        }
        int idx = word.charAt(0)-'a';
        if (node.children[idx]==null) node.children[idx] = new TrieNode();
        insert(word.substring(1), node.children[idx]);
    }
    
    public boolean search(String word) {
   
        return search(word, root);
    }
    private boolean search(String word, TrieNode node) {
   
        if (node==null) return false;
        if (word.length()==0) return node.isWord;
        int idx = word.charAt(0)-'a';
        return search(word.substring(1), node.children[idx]);
    }
    
    public boolean startsWith(String prefix) {
   
        return startsWith(prefix, root);
    }
    private boolean startsWith(String prefix, TrieNode node) {
   
        if (node==null) return false;
        if (prefix.length()==0) return true;
        int idx = prefix.charAt(0)-'a';
        return startsWith(prefix.substring(1), node.children[idx]);
    }
}

Leetcode-677. 键值映射

实现一个 MapSum 类里的两个方法,insert 和 sum。

对于方法 insert,你将得到一对(字符串,整数)的键值对。字符串表示键,整数表示值。如果键已经存在,那么原来的键值对将被替代成新的键值对。

对于方法 sum,你将得到一个表示前缀的字符串,你需要返回所有以该前缀开头的键的值的总和。

示例 1:

输入: insert("apple", 3), 输出: Null
输入: sum("ap"), 输出: 3
输入: insert("app", 2), 输出: Null
输入: sum("ap"), 输出: 5

解法:

  • Java
class MapSum {
   
    
    class TrieNode {
   
        int val = 0;
        TrieNode[] children = new TrieNode[26];
    }
    
    private TrieNode root = new TrieNode();
    
    public MapSum() {
   }
    
    public void insert(String key, int val) {
   
        insert(key, val, root);
    }
    private void insert(String key, int val, TrieNode node) {
   
        if (node==null) return;
        if (key.length()==0) {
   
            node.val = val;
            return;
        }
        int idx = key.charAt(0)-'a';
        if (node.children[idx]==null) node.children[idx] = new TrieNode();
        insert(key.substring(1), val, node.children[idx]);
    }
    
    public int sum(String prefix) {
   
        return sum(prefix, root);
    }
    
    private int sum(String prefix, TrieNode node) {
   
        if (node==null) return 0;
        if (prefix.length()!=0) {
   
            int idx = prefix.charAt(0)-'a';
            return sum(prefix.substring(1), node.children[idx]);
        }
        int res = node.val;
        for (TrieNode child :node.children) {
   
            res += sum("", child);
        }
        return res;
    }
}