【算法题解】34. 二叉树的最小深度

蔚落 2024-03-16 13:45 151阅读 0赞

这是一道 简单

https://leetcode.cn/problems/minimum-depth-of-binary-tree/

文章目录

    • 题目
    • 简单递归解法
        • Java 代码实现
        • Go 代码实现
        • 复杂度分析
    • DFS
        • Java 代码实现
        • Go 代码实现
        • 复杂度分析
    • BFS
        • Java 代码实现
        • Go 代码实现
        • 复杂度分析
    • 总结

题目

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

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

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

示例 1:

8550e78685022eb28bf1dac398fa1060.png

  1. 输入:root = [3,9,20,null,null,15,7]
  2. 输出:2

示例 2:

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

提示:

  • 树中节点数的范围在 [ 0 , 1 0 5 ] [0, 10^5] [0,105] 内
  • − 1000 < = N o d e . v a l < = 1000 -1000 <= Node.val <= 1000 −1000<=Node.val<=1000

简单递归解法

这道题目和 二叉树的最大深度 一样,都可以使用递归解法。

求解公式为: 二叉树的最小深度 = M i n Min Min(左子树的最小深度,右子树的最小深度) + 1

但是需要注意的是,只有左子树和右子树都不为空的时候才能使用上述公式求解。

如果左子树为空,那么二叉树的最小深度 = 右子树的最小深度 + 1。如下图所示:
image.png
同样的如果右子树为空,那么二叉树的最小深度 = 左子树的最小深度 + 1

极端情况下会退化成链表,如下图所示:
image.png

Java 代码实现
  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. public int minDepth(TreeNode root) {
  18. if(root == null){
  19. return 0;
  20. }
  21. // 没有子树
  22. if(root.left == null && root.right == null){
  23. return 1;
  24. }
  25. int leftDepth = minDepth(root.left);
  26. int rightDepth = minDepth(root.right);
  27. if(leftDepth == 0){
  28. return rightDepth + 1;
  29. }else if(rightDepth == 0){
  30. return leftDepth + 1;
  31. }else {
  32. return Math.min(leftDepth, rightDepth) + 1;
  33. }
  34. }
  35. }
Go 代码实现
  1. /**
  2. * Definition for a binary tree node.
  3. * type TreeNode struct {
  4. * Val int
  5. * Left *TreeNode
  6. * Right *TreeNode
  7. * }
  8. */
  9. func minDepth(root *TreeNode) int {
  10. if root == nil {
  11. return 0
  12. }
  13. if root.Left == nil && root.Right == nil {
  14. return 1
  15. }
  16. leftMin := minDepth(root.Left)
  17. rightMin := minDepth(root.Right)
  18. if root.Left == nil {
  19. return rightMin + 1
  20. }else if root.Right == nil {
  21. return leftMin + 1
  22. }else{
  23. return min(leftMin, rightMin) + 1
  24. }
  25. }
  26. func min(a int, b int) int {
  27. if a < b {
  28. return a
  29. }else {
  30. return b
  31. }
  32. }
复杂度分析
  • 时间复杂度: O ( N ) O(N) O(N),N 为二叉树中节点的个数,每个节点都需要计算一次,总共 N 次。
  • 空间复杂度: O ( N ) O(N) O(N),N 为二叉树中节点的个数,空间复杂度为调用栈的深度,最多为 N ,即二叉树退化成链表的时候。

DFS

相较于上一步的简单递归解法,深度优先遍历的优点是可以进行剪枝,如下图所示:
image.png
root节点3节点9 的深度是2,那么在遍历到 节点20 的时候深度已经达到2了,后面就无需再遍历,可以直接返回了。

另外需要注意的是边界条件和还原现场:

  • 边界条件:碰到叶子结点时计算一次最小深度,并返回。
  • 还原现场depth--,每次回退到上一层的时候深度跟着要减一。
Java 代码实现
  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. private int depth = 1;
  18. private int ans = Integer.MAX_VALUE;
  19. public int minDepth(TreeNode root) {
  20. if(root == null){
  21. return 0;
  22. }
  23. dfs(root);
  24. return ans;
  25. }
  26. private void dfs(TreeNode node){
  27. // 剪枝
  28. if(depth >= ans){
  29. return;
  30. }
  31. // 边界条件,碰到叶子结点计算一次最小深度
  32. if(node.left == null && node.right == null){
  33. ans = Math.min(ans, depth);
  34. return;
  35. }
  36. depth++;
  37. // 遍历左子树
  38. if(node.left != null){
  39. dfs(node.left);
  40. }
  41. // 遍历右子树
  42. if(node.right != null){
  43. dfs(node.right);
  44. }
  45. // 还原现场
  46. depth--;
  47. }
  48. }
Go 代码实现
  1. /**
  2. * Definition for a binary tree node.
  3. * type TreeNode struct {
  4. * Val int
  5. * Left *TreeNode
  6. * Right *TreeNode
  7. * }
  8. */
  9. var (
  10. depth int
  11. ans int
  12. )
  13. func minDepth(root *TreeNode) int {
  14. if root == nil {
  15. return 0
  16. }
  17. depth = 1
  18. ans = 2 << 32 - 1
  19. dfs(root)
  20. return ans
  21. }
  22. func dfs(node *TreeNode) {
  23. // 剪枝
  24. if depth >= ans {
  25. return
  26. }
  27. // 边界条件
  28. if node.Left == nil && node.Right == nil {
  29. ans = min(ans, depth)
  30. return
  31. }
  32. depth++
  33. if(node.Left != nil){
  34. dfs(node.Left)
  35. }
  36. if(node.Right != nil){
  37. dfs(node.Right)
  38. }
  39. // 还原现场
  40. depth--
  41. }
  42. func min(a int, b int) int {
  43. if a < b {
  44. return a
  45. }else {
  46. return b
  47. }
  48. }
复杂度分析
  • 时间复杂度: O ( N ) O(N) O(N),N 为二叉树中的节点个数,最差的情况是每个节点都需要遍历一次,总计 N 次。
  • 空间复杂度: O ( N ) O(N) O(N),N 为二叉树中节点的个数,空间复杂度为调用栈的深度,最多为 N 层。

BFS

广度优先遍历,一层一层的逐层遍历,遇到的第一个叶子结点的深度就是最小深度。

以下图为例,当遍历到 节点9 的时候,就可以直接返回了。
image.png

Java 代码实现
  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. public int minDepth(TreeNode root) {
  18. if(root == null){
  19. return 0;
  20. }
  21. Queue<TreeNode> queue = new LinkedList<>();
  22. int depth = 1;
  23. queue.offer(root);
  24. while(!queue.isEmpty()){
  25. // 同一层节点个数
  26. int len = queue.size();
  27. // 将同一层节点全部取出,并将下一层节点入队
  28. for(int i = 0; i < len; i++){
  29. TreeNode node = queue.poll();
  30. // 第一次遇到叶子结点,就是最小深度
  31. if(node.left == null && node.right == null){
  32. return depth;
  33. }
  34. // 左子树入队
  35. TreeNode leftNode = node.left;
  36. if(leftNode != null){
  37. queue.offer(leftNode);
  38. }
  39. // 右子树入队
  40. TreeNode rightNode = node.right;
  41. if(rightNode != null){
  42. queue.offer(rightNode);
  43. }
  44. }
  45. // 同一层节点全部取出后,深度加一
  46. depth++;
  47. }
  48. return depth;
  49. }
  50. }
Go 代码实现
  1. /**
  2. * Definition for a binary tree node.
  3. * type TreeNode struct {
  4. * Val int
  5. * Left *TreeNode
  6. * Right *TreeNode
  7. * }
  8. */
  9. func minDepth(root *TreeNode) int {
  10. if root == nil {
  11. return 0
  12. }
  13. queue := []*TreeNode{
  14. root}
  15. depth := 1
  16. for len(queue) > 0 {
  17. // 同一层节点个数
  18. len := len(queue)
  19. // 将同一层节点全部取出,并将下一层节点入队
  20. for i := 0; i < len; i++ {
  21. node := queue[0]
  22. queue = queue[1:]
  23. // 第一次遇到叶子结点,就是最小深度
  24. if node.Left == nil && node.Right == nil {
  25. return depth
  26. }
  27. // 左子树入队
  28. if node.Left != nil {
  29. queue = append(queue, node.Left)
  30. }
  31. // 右子树入队
  32. if node.Right != nil {
  33. queue = append(queue, node.Right)
  34. }
  35. }
  36. // 同一层节点全部取出后,深度加一
  37. depth++
  38. }
  39. return depth
  40. }
复杂度分析
  • 时间复杂度: O ( N ) O(N) O(N),N 为二叉树中节点的个数,最坏的情况下需要遍历每一个节点。
  • 空间复杂度: O ( N ) O(N) O(N),N 为二叉树中节点的个数,空间复杂度主要取决于队列的大小,最差的情况就是放 N 个元素。

总结

简单递归解法 的思路最为简单,但是需要计算所有节点,所以效率上不是最好的。

DFSBFS 虽然时间复杂度上也都是 O ( N ) O(N) O(N),但这是在最坏的情况下得到的,通常都不需要遍历所有节点,所以效率要高一些。

因为 BFSDFS 所需要遍历的节点数会更少一些,所以个人觉得求最小深度(最短路径)的题目使用 BFS 更为合适一些。

发表评论

表情:
评论列表 (有 0 条评论,151人围观)

还没有评论,来说两句吧...

相关阅读