Leetcode 题解 - 树

参考☞:CS-Notes



递归

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



1. 树的高度

Leetcode (opens new window)/ 力扣

class Solution {
    public int maxDepth(TreeNode root) {
        // 结束条件
        if (root == null) {
            return 0;
        }

        // 本级递归要做的事
        // 左子树高度
        int leftDepth = maxDepth(root.left);
        // 右子树高度
        int rightDepth = maxDepth(root.right);

        // 返回值
        // 左右子树最高的就是树的高度
        return Math.max(leftDepth, rightDepth) + 1;
    }
}


2. 平衡树

Leetcode (opens new window)/ 力扣

class Solution {
    // 标志位,如果为 true 就说明是平衡二叉树,否则反之
    private boolean flag = true;

    public boolean isBalanced(TreeNode root) {
        maxDepth(root);

        return flag;
    }
    
    /**
     * 递归计算树的高度
     */
    public int maxDepth(TreeNode root) {
        // 结束条件
        if (root == null) {
            return 0;
        }

        // 本级递归要做的事
        // 得到左右子树高度
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);

        // 如果左右两个子树的高度差大于 1
        // 将标志位置为 false
        if (Math.abs(leftDepth - rightDepth) > 1) {
            flag = false;
        }

        // 返回值
        // 返回左右子树的最大高度
        return Math.max(leftDepth, rightDepth) + 1;
    }
}


3. 两节点的最长路径

Leetcode (opens new window)/ 力扣

class Solution {
    // 直径长度
    private int max = 0;

    public int diameterOfBinaryTree(TreeNode root) {
        depth(root);

        return max;
    }

    public int depth(TreeNode root) {
        // 结束条件
        if (root == null) {
            return 0;
        }

        // 本级递归要做的事
        int leftDepth = depth(root.left);
        int rightDepth = depth(root.right);

        // 一棵二叉树的直径长度是任意两个结点路径长度中的最大值
        max = Math.max(max, leftDepth + rightDepth);

        // 返回值
        return Math.max(leftDepth, rightDepth) + 1;
    }
}


4. 翻转树

Leetcode (opens new window)/ 力扣

/**
 * 递归实现
 * 思想:翻转左子树和右子树就完事了
 */
class Solution {
    public TreeNode invertTree(TreeNode root) {
        // 结束条件
        if (root == null) {
            return root;
        }

        // 本级递归要做的事
        // 因为 root.left 的值会改变
        // 所以需要提前记录下来
        TreeNode node = root.left;
        root.left = invertTree(root.right);
        root.right = invertTree(node);

        // 返回值
        return root;
    }
}


5. 归并两棵树

Leetcode (opens new window)/ 力扣

/**
 * 递归实现
 */
class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        // 结束条件
        if (root1 == null && root2 == null) {
            return null;
        }
        if (root1 == null) {
            return root2;
        }
        if (root2 == null) {
            return root1;
        }

        // 本级递归要做的事
        TreeNode root = new TreeNode(root1.val + root2.val);
        root.left = mergeTrees(root1.left, root2.left);
        root.right = mergeTrees(root1.right, root2.right);

        // 返回值
        return root;
    }
}


6. 判断路径和是否等于一个数

Leetcode (opens new window)/ 力扣

/**
 * 递归实现
 * 思想:从根节点开始找,从左右子树分别开始递归
 */
class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        // 结束条件
        if (root == null) {
            return false;
        }

        // 本级递归要做的事
        if (root.left == null && root.right == null && root.val == targetSum) {
            return true;
        }

        // 返回值
        return hasPathSum(root.left, targetSum - root.val) || 
               hasPathSum(root.right, targetSum - root.val);

    }
}


7. 统计路径和等于一个数的路径数量

Leetcode (opens new window)/ 力扣

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

        // 三种情况相加
        int result = pathSumStartWithRoot(root, targetSum) + 
                     pathSum(root.left, targetSum) + 
                     pathSum(root.right, targetSum);
    
        return result;
    }

    public int pathSumStartWithRoot(TreeNode root, int targetSum) {
        if (root == null) {
            return 0;
        }

        int result = 0;
        if (root.val == targetSum) {
            result++;
        }

        result +=  pathSumStartWithRoot(root.left, targetSum - root.val) + 
                   pathSumStartWithRoot(root.right, targetSum - root.val);

        return result;
    }
}


8. 子树

Leetcode (opens new window)/ 力扣

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

        return isSameTree(root, subRoot) ||
               isSubtree(root.left, subRoot) ||
               isSubtree(root.right, subRoot);
    }


    public boolean isSameTree(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 isSameTree(s.left, t.left) && isSameTree(s.right, t.right); 
    }
}


9. 树的对称

Leetcode (opens new window)/ 力扣

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }

        return isSymmetric(root.left, root.right);
    }

    /**
     * 方法重载
     * 递归解决
     */
    public boolean isSymmetric(TreeNode t1, TreeNode t2) {
        // 结束条件
        // 只有两个节点同时为空时,才返回 true,说明每个值都相同
        if (t1 == null && t2 == null) {
            return true;
        }
        if (t1 == null || t2 == null) {
            return false;
        }

        // 本级递归要做的事
        if (t1.val != t2.val) {
            return false;
        }

        // 返回值
        return isSymmetric(t1.left, t2.right) &&
               isSymmetric(t1.right, t2.left);
    }
}


10. 最小路径

Leetcode (opens new window)/ 力扣

class Solution {
    public int minDepth(TreeNode root) {
        // 结束条件
        if (root == null) {
            return 0;
        }

        // 本级递归要做的事
        int leftDepth = minDepth(root.left);
        int rightDepth = minDepth(root.right);

        // 返回值
        if (leftDepth == 0 || rightDepth == 0) {
            return leftDepth + rightDepth + 1;
        }
        return Math.min(leftDepth, rightDepth) + 1;
    }
}


11. 统计左叶子节点的和

Leetcode (opens new window)/ 力扣

class Solution {
    /**
     * 左节点定义:如果左节点不为空,且左节点没有左右孩子,那么这个节点就是左叶子。
     * 判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。
     * 
     */
    public int sumOfLeftLeaves(TreeNode root) {
        if (root == null) {
            return 0;
        }

        int result = 0;
        if (root.left != null && root.left.left == null && root.left.right == null) {
            result += root.left.val;
        }

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


12. 相同节点值的最大路径长度

Leetcode (opens new window)/ 力扣

class Solution {

    // 返回结果
    private int result;

    public int longestUnivaluePath(TreeNode root) {
        result = 0;
        arrowLength(root);
        return result;   
    }

    public int arrowLength(TreeNode root) {
        if (root == null) {
             return 0;
        }

        int left = arrowLength(root.left);
        int right = arrowLength(root.right);
        int arrowLeft = 0, arrowRight = 0;

        if (root.left != null && root.left.val == root.val) {
            arrowLeft += left + 1;
        }
        if (root.right != null && root.right.val == root.val) {
            arrowRight += right + 1;
        }

        result = Math.max(result, arrowLeft + arrowRight);

        return Math.max(arrowLeft, arrowRight);
    }
}


13. 间隔遍历

Leetcode (opens new window)/ 力扣

参考题解☞:https://leetcode-cn.com/problems/house-robber-iii/solution/san-chong-fang-fa-jie-jue-shu-xing-dong-tai-gui-hu/

(1)原始代码,使用递归

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

        // 两个组合(求最大值):
        // (1)爷爷节点和四个孙子节点的和
        // (2)两个儿子节点的和
        // 爷爷节点的值
        int money = root.val;

        // 将四个孙子节点的值和爷爷节点的值相加
        if (root.left != null) {
            money += rob(root.left.left) + rob(root.left.right);
        }
        if (root.right != null) {
            money += rob(root.right.left) + rob(root.right.right);
        }

        // 比较两个组合的最大值,然后返回
        return Math.max(money, rob(root.left) + rob(root.right));
    }
}

(2)进行优化,将以某个节点为根的最大值用 map 保存起来,避免重复计算,提升效率

class Solution {
    public int rob(TreeNode root) {
        Map<TreeNode, Integer> map = new HashMap<>();
        return robInternal(root, map);
    }

    public int robInternal(TreeNode root, Map<TreeNode, Integer> map) {
        if (root == null) {
            return 0;
        }
        // 如果有就直接取出来,避免重复计算
        if (map.containsKey(root)) {
            return map.get(root);
        }
        int money = root.val;

        if (root.left != null) {
            money += robInternal(root.left.left, map) + robInternal(root.left.right, map);
        }
        if (root.right != null) {
            money += robInternal(root.right.left, map) + robInternal(root.right.right, map);
        }

        int result = Math.max(money, robInternal(root.left, map) + robInternal(root.right, map));
        // 将结果存入 map 中
        map.put(root, result);

        return result;
    }
}

(3)再次优化

class Solution {
    // 动态规划
    public int rob(TreeNode root) {
        int[] result = robInternal(root);

        return Math.max(result[0], result[1]);
    }

    public int[] robInternal(TreeNode root) {
        if (root == null) {
            return new int[2];
        }

        int[] result = new int[2];

        int[] left = robInternal(root.left);
        int[] right = robInternal(root.right);

        result[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        result[1] = root.val + left[0] + right[0];

        return result;
    }
}


14. 找出二叉树中第二小的节点

Leetcode (opens new window)/ 力扣

class Solution {
    
    private int result = -1;

    public int findSecondMinimumValue(TreeNode root) {
        // 因为 root.val = min(root.left.val, root.right.val)
        // 所以最小值必是 root 节点的值,即最小值会不断往上传递 
        dfs(root, root.val);
        return result;
    }

    public void dfs(TreeNode root, int val) {
        if (root == null) {
            return ;
        }

        if (root.val != val) {
            if (result == -1) {
                result = root.val;
            } else {
                result = Math.min(result, root.val);
            }

            return ;
        }

        dfs(root.left, val);
        dfs(root.right, val);
    }
}


层次遍历

1. 二叉树的层序遍历

Leetcode (opens new window)/ 力扣

class Solution {
    // BFS
    public List<List<Integer>> levelOrder(TreeNode root) {
        if (root == null) {
            return new ArrayList<List<Integer>>();
        }

        List<List<Integer>> result = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);

        while (!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> level = new ArrayList<>();

            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                level.add(node.val);

                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }

            result.add(level);
        }

        return result;
    }
}

2. 一棵树每层节点的平均数

Leetcode (opens new window)/ 力扣

class Solution {
    // DFS
    public List<Double> averageOfLevels(TreeNode root) {
        // 记录每层的总和
        List<Double> sums = new ArrayList<>();
        // 记录每层的节点数量
        List<Integer> counts = new ArrayList<>();

        dfs(root, 0, counts, sums);

        List<Double> averages = new ArrayList<>();
 
        for (int i = 0; i < sums.size(); i++) {
            averages.add(i, sums.get(i) / counts.get(i));
        }

        return averages;
    }

    public void dfs(TreeNode root, int level, List<Integer> counts, List<Double> sums) {
        if (root == null) {
            return ;
        }

        // 需要考虑第一次添加和
        // 第 N 次添加两种情况
        if (level < sums.size()) {
            sums.set(level, sums.get(level) + root.val);
            counts.set(level, counts.get(level) + 1);
        } else {
            sums.add(root.val * 1.0);
            counts.add(1);
        }

        dfs(root.left, level + 1, counts, sums);
        dfs(root.right, level + 1, counts, sums);
    }
}


3. 得到左下角的节点

Leetcode (opens new window)/ 力扣

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

        Queue<TreeNode> queue = new LinkedList<>();

        queue.add(root);

        while (!queue.isEmpty()) {
            root = queue.poll();
            
            if (root.right != null) {
                queue.add(root.right);
            }

            if (root.left != null) {
                queue.add(root.left);
            }
        }

        return root.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 实现。

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

(1)前序

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

(2)中序

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

(3)后序

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

(4)层次遍历

/**
 * 思想:就是把每个节点加入队列,每个节点出队列的时候把它的子节点都加入队列。
 * 队列:先进先出
 */
void bfs(TreeNode root) {
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        // Java 的 pop 写作 poll()
        TreeNode node = queue.poll(); 
        if (node.left != null) {
            queue.add(node.left);
        }
        if (node.right != null) {
            queue.add(node.right);
        }
    }
}

1. 实现二叉树的前序遍历

Leetcode (opens new window)/ 力扣

(1)递归实现

class Solution {
    // 递归实现
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();

        preOrder(root, result);

        return result;
    }

    public void preOrder(TreeNode root, List<Integer> result) {
        if (root == null) {
            return ;
        }

        result.add(root.val);

        preOrder(root.left, result);
        preOrder(root.right, result);
    }
}

(2)非递归实现

class Solution {
    // 非递归实现,栈
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();

        if (root != null) {
            stack.push(root);
        }

        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();

            if (node == null) {
                continue;
            }

            result.add(node.val);

            // 栈:先进后出
            stack.push(node.right);
            stack.push(node.left);
        }

        return result;
    }
}


2. 实现二叉树的后序遍历

Leetcode (opens new window)/ 力扣

class Solution {
    // 非递归实现
    public List<Integer> postorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();

        if (root != null) {
            stack.push(root);
        }

        List<Integer> result = new ArrayList<>();

        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            if (node == null) {
                continue;
            }
            result.add(node.val);
            stack.add(node.left);
            stack.add(node.right);

        }

        Collections.reverse(result);

        return result;
    }   
}


3. 实现二叉树的中序遍历

(1)递归实现

class Solution {
    // 递归实现
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();

        dfs(root, result);

        return result;
    }

    public void dfs(TreeNode root, List<Integer> result) {
        if (root == null) {
            return ;
        }

        dfs(root.left, result);
        result.add(root.val);
        dfs(root.right, result);
    }
}


BST

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

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



1. 修剪二叉查找树

Leetcode (opens new window)/ 力扣

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) {
            return null;
        }

        // 1 如果 root.val 小于 low 的话
        // 就修剪 root 和 root 的左节点,因为 root.val 是大于左子树的所有节点的
        // 因为这是 BST(二叉查找树)
        if (root.val < low) {
            root = root.right;
            root = trimBST(root, low, high);
        // 与 1 同理    
        } else if (root.val > high) {
            root = root.left;
            root = trimBST(root, low, high);
        } else {
            // 说明 root 满足区间条件
            // 检查左右节点是否满足条件
            root.left = trimBST(root.left, low, high);
            root.right = trimBST(root.right, low, high);
        }

        return root;
    }
}

简洁写法

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) {
            return null;
        }

        if (root.val < low) {
            return trimBST(root.right, low, high);
        }
        if (root.val > high) {
            return trimBST(root.left, low, high);
        }

        root.left = trimBST(root.left, low, high);
        root.right = trimBST(root.right, low, high);

        return root;
    }
}


2. 寻找二叉查找树的第 k 个元素

Leetcode (opens new window)/ 力扣

中序遍历解法

class Solution {
    private int count = 0;
    private int val = 0;

    public int kthSmallest(TreeNode root, int k) {
        inOrder(root, k);
        return val;
    }

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


3. 把二叉搜索树转换为累加树

Leetcode (opens new window)/ 力扣

class Solution {
    private int num = 0;
    
    public TreeNode convertBST(TreeNode root) {
        if (root != null) {
            // 先遍历右子树
            convertBST(root.right);
            // 遍历顶点
            root.val = root.val + num;
            num = root.val;
            // 遍历左子树
            convertBST(root.left);
            return root;
        }

        return null;
    }
}


4. 二叉查找树的最近公共祖先

Leetcode (opens new window)/ 力扣

class Solution {
    /**
     * 二叉搜索树特性:
     *      左子树比根节点小,右子树比根节点大
     * 三种情况:
     *      (1)p,q 都小于 root,去左子树中找最近公共祖先
     *      (2)p,q 都大于 root,去右子树中找最近公共祖先
     *      (3)p,q 分布在左右子树,说明 root 就是最近公共祖先
     * 
     * 递归思想,把树就看成 左-中-右 3 个节点
     */
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (p.val < root.val && q.val < root.val) {
            return lowestCommonAncestor(root.left, p, q);
        } 
        if (p.val > root.val && q.val > root.val) {
            return lowestCommonAncestor(root.right, p, q);
        }

        return root;
    }
}


5. 二叉树的最近公共祖先

Leetcode (opens new window)/ 力扣

/**
 * 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) {
            return null;
        }

        // 如果 p, q 有一个等于 root,说明 root 就是最近公共祖先
        if (p.val == root.val || q.val == root.val) {
            return root;
        }
        // 看 p, q 是否都在左子树中,去左子树中找最近公共祖先
        if (find(root.left, p.val) && find(root.left, q.val)) {
            return lowestCommonAncestor(root.left, p, q);
        }
        // 看 p, q 是否都在右子树中,去右子树中找最近公共祖先
        if (find(root.right, p.val) && find(root.right, q.val)) {
            return lowestCommonAncestor(root.right, p, q);
        }

        // 如果都不满足条件,说明 p, q 分别在左右子树,root 就是最近公共祖先
        return root;
    }

    // 前序遍历
    private boolean find(TreeNode root, int val) {
        if (root == null) {
            return false;
        }

        if (root.val == val) {
            return true;
        }

        return find(root.left, val) || find(root.right, val);
    }
}

简洁写法

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

        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        if (left == null) {
            return right;
        }
        if (right == null) {
            return left;
        }

        return root;
    }
}


6. 从有序数组中构造二叉查找树

Leetcode (opens new window)/ 力扣

// 中序遍历,以数组的中间元素作为根节点
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return dfs(nums, 0, nums.length - 1);
    }

    public TreeNode dfs(int[] nums, int low, int high) {
        if (low > high) {
            return null;
        }

        // 不用 (low + high) /2 避免溢出
        int mid = low + ((high - low) >> 1);

        TreeNode root = new TreeNode(nums[mid]);
        root.left = dfs(nums, low, mid - 1);
        root.right = dfs(nums, mid + 1, high);

        return root;
        
    }
}


7. 根据有序链表构造平衡的二叉查找树

Leetcode (opens new window)/ 力扣

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        if (head == null) {
            return null;
        } else if (head.next == null) {
            return new TreeNode(head.val);
        }
        ListNode pre = head;
        ListNode p = pre.next;
        ListNode q = p.next;
        
        // 找到链表的中点 p
        while(q != null && q.next != null){
            pre = pre.next;
            p = pre.next;
            q = q.next.next;
        }
        // 将中点左边的链表分开
        pre.next = null;
        TreeNode root = new TreeNode(p.val);
        root.left = sortedListToBST(head);
        root.right = sortedListToBST(p.next);
        return root;
    }
}


8、两数之和 IV - 输入 BST

Leetcode (opens new window)/ 力扣

class Solution {
    public boolean findTarget(TreeNode root, int k) {
        if (root == null) {
            return false;
        }

        List<Integer> list = new ArrayList<>();
        
        inOrder(root, list);

        // 首索引
        int low = 0;
        // 尾索引
        int high = list.size() - 1;

        // list 中的元素是有序的
        while (low < high) {
            if (list.get(low) + list.get(high) == k) {
                return true;
            // 说明不够大,low 得前移一位(有序)    
            } else if (list.get(low) + list.get(high) < k) {
                low++;
            // 说明超过范围了,high 得后移一位(有序)
            } else {
                high--;
            }
        }

        return false;
    }

    // 因为中序遍历的元素是有序的,维护一个 List 集合
    public void inOrder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return ;
        }

        inOrder(root.left, list);
        list.add(root.val);
        inOrder(root.right, list);
    }
}


9. 在二叉查找树中查找两个节点之差的最小绝对值

Leetcode (opens new window)/ 力扣

class Solution {
    private Integer pre;
    private Integer result;

    public int getMinimumDifference(TreeNode root) {
        result = Integer.MAX_VALUE;
        pre = -1;

        dfs(root);

        return result;
    }

    // 中序遍历,相邻节点为最小值
    public void dfs(TreeNode root) {
        if (root == null) {
            return ;
        }

        dfs(root.left);

        if (pre == -1) {
            pre = root.val;
        } else {
            result = Math.min(result, root.val - pre);
            pre = root.val;
        }

        dfs(root.right);
    }
}


10. 寻找二叉查找树中出现次数最多的值

Leetcode (opens new window)/ 力扣

class Solution {

    // 当前值的个数
    private int curCount = 1;

    // 最多值的个数
    private int maxCount = 1;

    private TreeNode pre = null;

    public int[] findMode(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        dfs(root, result);

        // 将集合转换成数组
        int[] res = new int[result.size()];
        for (int i = 0; i < result.size(); i++) {
            res[i] = result.get(i);
        }

        return res;
    }

    // 深度优先遍历
    public void dfs(TreeNode root, List<Integer> result) {
        if (root == null) {
            return ;
        }

        dfs(root.left, result);

        if (pre != null) {
            // 如果相等就累加当前值的个数
            if (pre.val == root.val) {
                curCount++;
            // 不相等说明值已经不同,将累加值重置为 1
            } else {
                curCount = 1;
            }
        }

        // 如果当前值的个数大于原先值的个数,说明这个才是众数
        // 清空集合,将当前值加入到集合中,同时改变 maxCount
        if (curCount > maxCount) {
            maxCount = curCount;
            result.clear();
            result.add(root.val);
        // 相等的话说明这两个都是众数
        } else if (curCount == maxCount) {
            result.add(root.val);
        }

        pre = root;

        dfs(root.right, result);
    }
}


Trie


img


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

1. 实现一个 Trie

Leetcode (opens new window)/ 力扣

class Trie {
    // 根节点
    private Node root;

    private class Node {
        // 孩子节点,因为有 26 个字母,所以初始化 26 个空间
        Node[] childs = new Node[26];
        // 默认值为 false
        boolean isLeaf;
    }

    public Trie() {
        root = new Node();
    }
    
    // 插入方法
    public void insert(String word) {
        Node p = root;

        for (int i = 0; i < word.length(); i++) {
            int u = word.charAt(i) - 'a';

            // 如果为空
            if (p.childs[u] == null) {
                // 就初始化空间
                p.childs[u] = new Node();
            }
            p = p.childs[u];
        }

        p.isLeaf = true;
    }
    
    // 查找方法
    public boolean search(String word) {
        Node p = root;

        for (int i = 0; i < word.length(); i++) {
            int u = word.charAt(i) - 'a';

            // 如果空间为 null,说明没有值,返回 false
            if (p.childs[u] == null) {
                return false;
            }
            p = p.childs[u];
        }

        // 如果都跑完了,还需要判断是否是最后一个节点
        return p.isLeaf;
    }
    
    // 是否是已经插入的字符串的前缀之一
    public boolean startsWith(String prefix) {
        Node p = root;

        for (int i = 0; i < prefix.length(); i++) {
            int u = prefix.charAt(i) - 'a';

            // 如果为空,说明没有值,直接返回 false
            if (p.childs[u] == null) {
                return false;
            }

            p = p.childs[u];
        }

        return true;
    }
}


2. 实现一个 Trie,用来求前缀和

Leetcode (opens new window)/ 力扣

class MapSum {
    // 根节点
    private Node root;

    private class Node {
        // 子节点
        Node[] childs = new Node[26];
        // 值
        int val;
    }

    // 初始化s
    public MapSum() {
        root = new Node();
    }
    
    // 插入方法
    public void insert(String key, int val) {
        Node p = root;

        for (int i = 0; i < key.length(); i++) {
            int u = key.charAt(i) - 'a';
            
            if (p.childs[u] == null) {
                p.childs[u] = new Node();
            }

            p = p.childs[u];
        }

        p.val = val;
    }
    
    // 求前缀和方法
    public int sum(String prefix) {
        Node p = root;

        for (int i = 0; i < prefix.length(); i++) {
            int u = prefix.charAt(i) - 'a';

            // 没有这个键
            if (p.childs[u] == null) {
                return 0;
            }

            p = p.childs[u];
        }

        return dfs(p);
    }

    public int dfs(Node root) {
        if (root == null) {
            return 0;
        }
        
        int result = 0;
        
        for (int i = 0; i < 26; i++) {
            if (root.childs[i] != null) {
                result += dfs(root.childs[i]);
            }
        }

        return result + root.val;
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值