mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2026-02-02 18:39:09 +08:00
716 lines
20 KiB
Markdown
716 lines
20 KiB
Markdown
<p align="center">
|
||
<a href="https://programmercarl.com/other/xunlianying.html" target="_blank">
|
||
<img src="../pics/训练营.png" width="1000"/>
|
||
</a>
|
||
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
|
||
|
||
|
||
> 和求最大深度一个套路?
|
||
|
||
# 111.二叉树的最小深度
|
||
|
||
[力扣题目链接](https://leetcode.cn/problems/minimum-depth-of-binary-tree/)
|
||
|
||
给定一个二叉树,找出其最小深度。
|
||
|
||
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
|
||
|
||
说明: 叶子节点是指没有子节点的节点。
|
||
|
||
示例:
|
||
|
||
给定二叉树 [3,9,20,null,null,15,7],
|
||
|
||
|
||

|
||
|
||
返回它的最小深度 2.
|
||
|
||
## 算法公开课
|
||
|
||
**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[看起来好像做过,一写就错! | LeetCode:111.二叉树的最小深度](https://www.bilibili.com/video/BV1QD4y1B7e2),相信结合视频再看本篇题解,更有助于大家对本题的理解**。
|
||
|
||
## 思路
|
||
|
||
|
||
看完了这篇[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html),再来看看如何求最小深度。
|
||
|
||
直觉上好像和求最大深度差不多,其实还是差不少的。
|
||
|
||
本题依然是前序遍历和后序遍历都可以,前序求的是深度,后序求的是高度。
|
||
|
||
* 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
|
||
* 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始)
|
||
|
||
那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离 也同样是最小深度。
|
||
|
||
以下讲解中遍历顺序上依然采用后序遍历(因为要比较递归返回之后的结果,本文我也给出前序遍历的写法)。
|
||
|
||
本题还有一个误区,在处理节点的过程中,最大深度很容易理解,最小深度就不那么好理解,如图:
|
||
|
||

|
||
|
||
这就重新审题了,题目中说的是:**最小深度是从根节点到最近叶子节点的最短路径上的节点数量。**,注意是**叶子节点**。
|
||
|
||
什么是叶子节点,左右孩子都为空的节点才是叶子节点!
|
||
|
||
### 递归法
|
||
|
||
来来来,一起递归三部曲:
|
||
|
||
1. 确定递归函数的参数和返回值
|
||
|
||
参数为要传入的二叉树根节点,返回的是int类型的深度。
|
||
|
||
代码如下:
|
||
|
||
```
|
||
int getDepth(TreeNode* node)
|
||
```
|
||
|
||
2. 确定终止条件
|
||
|
||
终止条件也是遇到空节点返回0,表示当前节点的高度为0。
|
||
|
||
代码如下:
|
||
|
||
```
|
||
if (node == NULL) return 0;
|
||
```
|
||
|
||
3. 确定单层递归的逻辑
|
||
|
||
这块和求最大深度可就不一样了,一些同学可能会写如下代码:
|
||
```
|
||
int leftDepth = getDepth(node->left);
|
||
int rightDepth = getDepth(node->right);
|
||
int result = 1 + min(leftDepth, rightDepth);
|
||
return result;
|
||
```
|
||
|
||
这个代码就犯了此图中的误区:
|
||
|
||

|
||
|
||
如果这么求的话,没有左孩子的分支会算为最短深度。
|
||
|
||
所以,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
|
||
|
||
反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。
|
||
|
||
代码如下:
|
||
|
||
```CPP
|
||
int leftDepth = getDepth(node->left); // 左
|
||
int rightDepth = getDepth(node->right); // 右
|
||
// 中
|
||
// 当一个左子树为空,右不为空,这时并不是最低点
|
||
if (node->left == NULL && node->right != NULL) {
|
||
return 1 + rightDepth;
|
||
}
|
||
// 当一个右子树为空,左不为空,这时并不是最低点
|
||
if (node->left != NULL && node->right == NULL) {
|
||
return 1 + leftDepth;
|
||
}
|
||
int result = 1 + min(leftDepth, rightDepth);
|
||
return result;
|
||
```
|
||
|
||
遍历的顺序为后序(左右中),可以看出:**求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑。**
|
||
|
||
整体递归代码如下:
|
||
```CPP
|
||
class Solution {
|
||
public:
|
||
int getDepth(TreeNode* node) {
|
||
if (node == NULL) return 0;
|
||
int leftDepth = getDepth(node->left); // 左
|
||
int rightDepth = getDepth(node->right); // 右
|
||
// 中
|
||
// 当一个左子树为空,右不为空,这时并不是最低点
|
||
if (node->left == NULL && node->right != NULL) {
|
||
return 1 + rightDepth;
|
||
}
|
||
// 当一个右子树为空,左不为空,这时并不是最低点
|
||
if (node->left != NULL && node->right == NULL) {
|
||
return 1 + leftDepth;
|
||
}
|
||
int result = 1 + min(leftDepth, rightDepth);
|
||
return result;
|
||
}
|
||
|
||
int minDepth(TreeNode* root) {
|
||
return getDepth(root);
|
||
}
|
||
};
|
||
```
|
||
|
||
精简之后代码如下:
|
||
|
||
```CPP
|
||
class Solution {
|
||
public:
|
||
int minDepth(TreeNode* root) {
|
||
if (root == NULL) return 0;
|
||
if (root->left == NULL && root->right != NULL) {
|
||
return 1 + minDepth(root->right);
|
||
}
|
||
if (root->left != NULL && root->right == NULL) {
|
||
return 1 + minDepth(root->left);
|
||
}
|
||
return 1 + min(minDepth(root->left), minDepth(root->right));
|
||
}
|
||
};
|
||
```
|
||
|
||
**精简之后的代码根本看不出是哪种遍历方式,所以依然还要强调一波:如果对二叉树的操作还不熟练,尽量不要直接照着精简代码来学。**
|
||
|
||
前序遍历的方式:
|
||
|
||
```CPP
|
||
class Solution {
|
||
private:
|
||
int result;
|
||
void getdepth(TreeNode* node, int depth) {
|
||
// 函数递归终止条件
|
||
if (node == nullptr) {
|
||
return;
|
||
}
|
||
// 中,处理逻辑:判断是不是叶子结点
|
||
if (node -> left == nullptr && node->right == nullptr) {
|
||
result = min(result, depth);
|
||
}
|
||
if (node->left) { // 左
|
||
getdepth(node->left, depth + 1);
|
||
}
|
||
if (node->right) { // 右
|
||
getdepth(node->right, depth + 1);
|
||
}
|
||
return ;
|
||
}
|
||
|
||
public:
|
||
int minDepth(TreeNode* root) {
|
||
if (root == nullptr) {
|
||
return 0;
|
||
}
|
||
result = INT_MAX;
|
||
getdepth(root, 1);
|
||
return result;
|
||
}
|
||
};
|
||
```
|
||
|
||
### 迭代法
|
||
|
||
相对于[104.二叉树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html),本题还可以使用层序遍历的方式来解决,思路是一样的。
|
||
|
||
如果对层序遍历还不清楚的话,可以看这篇:[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)
|
||
|
||
**需要注意的是,只有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点**
|
||
|
||
代码如下:(详细注释)
|
||
|
||
```CPP
|
||
class Solution {
|
||
public:
|
||
|
||
int minDepth(TreeNode* root) {
|
||
if (root == NULL) return 0;
|
||
int depth = 0;
|
||
queue<TreeNode*> que;
|
||
que.push(root);
|
||
while(!que.empty()) {
|
||
int size = que.size();
|
||
depth++; // 记录最小深度
|
||
for (int i = 0; i < size; i++) {
|
||
TreeNode* node = que.front();
|
||
que.pop();
|
||
if (node->left) que.push(node->left);
|
||
if (node->right) que.push(node->right);
|
||
if (!node->left && !node->right) { // 当左右孩子都为空的时候,说明是最低点的一层了,退出
|
||
return depth;
|
||
}
|
||
}
|
||
}
|
||
return depth;
|
||
}
|
||
};
|
||
```
|
||
|
||
|
||
## 其他语言版本
|
||
|
||
|
||
### Java:
|
||
|
||
```Java
|
||
class Solution {
|
||
/**
|
||
* 递归法,相比求MaxDepth要复杂点
|
||
* 因为最小深度是从根节点到最近**叶子节点**的最短路径上的节点数量
|
||
*/
|
||
public int minDepth(TreeNode root) {
|
||
if (root == null) {
|
||
return 0;
|
||
}
|
||
int leftDepth = minDepth(root.left);
|
||
int rightDepth = minDepth(root.right);
|
||
if (root.left == null) {
|
||
return rightDepth + 1;
|
||
}
|
||
if (root.right == null) {
|
||
return leftDepth + 1;
|
||
}
|
||
// 左右结点都不为null
|
||
return Math.min(leftDepth, rightDepth) + 1;
|
||
}
|
||
}
|
||
```
|
||
|
||
```java
|
||
class Solution {
|
||
/**
|
||
* 递归法(思路来自二叉树最大深度的递归法)
|
||
* 该题求最小深度,最小深度为根节点到叶子节点的深度,所以在迭代到每个叶子节点时更新最小值。
|
||
*/
|
||
int depth = 0;
|
||
// 定义最小深度,初始化最大值
|
||
int minDepth = Integer.MAX_VALUE;
|
||
public int minDepth(TreeNode root) {
|
||
dep(root);
|
||
return minDepth == Integer.MAX_VALUE ? 0 : minDepth;
|
||
}
|
||
void dep(TreeNode root){
|
||
if(root == null) return ;
|
||
// 递归开始,深度增加
|
||
depth++;
|
||
dep(root.left);
|
||
dep(root.right);
|
||
// 该位置表示递归到叶子节点了,需要更新最小深度minDepth
|
||
if(root.left == null && root.right == null)
|
||
minDepth = Math.min(minDepth , depth);
|
||
// 递归结束,深度减小
|
||
depth--;
|
||
}
|
||
}
|
||
```
|
||
|
||
```Java
|
||
class Solution {
|
||
/**
|
||
* 迭代法,层序遍历
|
||
*/
|
||
public int minDepth(TreeNode root) {
|
||
if (root == null) {
|
||
return 0;
|
||
}
|
||
Deque<TreeNode> deque = new LinkedList<>();
|
||
deque.offer(root);
|
||
int depth = 0;
|
||
while (!deque.isEmpty()) {
|
||
int size = deque.size();
|
||
depth++;
|
||
for (int i = 0; i < size; i++) {
|
||
TreeNode poll = deque.poll();
|
||
if (poll.left == null && poll.right == null) {
|
||
// 是叶子结点,直接返回depth,因为从上往下遍历,所以该值就是最小值
|
||
return depth;
|
||
}
|
||
if (poll.left != null) {
|
||
deque.offer(poll.left);
|
||
}
|
||
if (poll.right != null) {
|
||
deque.offer(poll.right);
|
||
}
|
||
}
|
||
}
|
||
return depth;
|
||
}
|
||
}
|
||
```
|
||
|
||
### Python :
|
||
|
||
递归法(版本一)
|
||
|
||
```python
|
||
class Solution:
|
||
def getDepth(self, node):
|
||
if node is None:
|
||
return 0
|
||
leftDepth = self.getDepth(node.left) # 左
|
||
rightDepth = self.getDepth(node.right) # 右
|
||
|
||
# 当一个左子树为空,右不为空,这时并不是最低点
|
||
if node.left is None and node.right is not None:
|
||
return 1 + rightDepth
|
||
|
||
# 当一个右子树为空,左不为空,这时并不是最低点
|
||
if node.left is not None and node.right is None:
|
||
return 1 + leftDepth
|
||
|
||
result = 1 + min(leftDepth, rightDepth)
|
||
return result
|
||
|
||
def minDepth(self, root):
|
||
return self.getDepth(root)
|
||
|
||
```
|
||
递归法(版本二)
|
||
|
||
```python
|
||
class Solution:
|
||
def minDepth(self, root):
|
||
if root is None:
|
||
return 0
|
||
if root.left is None and root.right is not None:
|
||
return 1 + self.minDepth(root.right)
|
||
if root.left is not None and root.right is None:
|
||
return 1 + self.minDepth(root.left)
|
||
return 1 + min(self.minDepth(root.left), self.minDepth(root.right))
|
||
|
||
|
||
```
|
||
递归法(版本三)前序
|
||
|
||
```python
|
||
class Solution:
|
||
def __init__(self):
|
||
self.result = float('inf')
|
||
|
||
def getDepth(self, node, depth):
|
||
if node is None:
|
||
return
|
||
if node.left is None and node.right is None:
|
||
self.result = min(self.result, depth)
|
||
if node.left:
|
||
self.getDepth(node.left, depth + 1)
|
||
if node.right:
|
||
self.getDepth(node.right, depth + 1)
|
||
|
||
def minDepth(self, root):
|
||
if root is None:
|
||
return 0
|
||
self.getDepth(root, 1)
|
||
return self.result
|
||
|
||
|
||
```
|
||
迭代法
|
||
|
||
```python
|
||
# Definition for a binary tree node.
|
||
# class TreeNode:
|
||
# def __init__(self, val=0, left=None, right=None):
|
||
# self.val = val
|
||
# self.left = left
|
||
# self.right = right
|
||
class Solution:
|
||
def minDepth(self, root: TreeNode) -> int:
|
||
if not root:
|
||
return 0
|
||
depth = 0
|
||
queue = collections.deque([root])
|
||
|
||
while queue:
|
||
depth += 1
|
||
for _ in range(len(queue)):
|
||
node = queue.popleft()
|
||
|
||
if not node.left and not node.right:
|
||
return depth
|
||
|
||
if node.left:
|
||
queue.append(node.left)
|
||
|
||
if node.right:
|
||
queue.append(node.right)
|
||
|
||
return depth
|
||
```
|
||
|
||
### Go:
|
||
|
||
```go
|
||
/**
|
||
* Definition for a binary tree node.
|
||
* type TreeNode struct {
|
||
* Val int
|
||
* Left *TreeNode
|
||
* Right *TreeNode
|
||
* }
|
||
*/
|
||
func min(a, b int) int {
|
||
if a < b {
|
||
return a;
|
||
}
|
||
return b;
|
||
}
|
||
// 递归
|
||
func minDepth(root *TreeNode) int {
|
||
if root == nil {
|
||
return 0;
|
||
}
|
||
if root.Left == nil && root.Right != nil {
|
||
return 1 + minDepth(root.Right);
|
||
}
|
||
if root.Right == nil && root.Left != nil {
|
||
return 1 + minDepth(root.Left);
|
||
}
|
||
return min(minDepth(root.Left), minDepth(root.Right)) + 1;
|
||
}
|
||
|
||
// 迭代
|
||
|
||
func minDepth(root *TreeNode) int {
|
||
dep := 0;
|
||
queue := make([]*TreeNode, 0);
|
||
if root != nil {
|
||
queue = append(queue, root);
|
||
}
|
||
for l := len(queue); l > 0; {
|
||
dep++;
|
||
for ; l > 0; l-- {
|
||
node := queue[0];
|
||
if node.Left == nil && node.Right == nil {
|
||
return dep;
|
||
}
|
||
if node.Left != nil {
|
||
queue = append(queue, node.Left);
|
||
}
|
||
if node.Right != nil {
|
||
queue = append(queue, node.Right);
|
||
}
|
||
queue = queue[1:];
|
||
}
|
||
l = len(queue);
|
||
}
|
||
return dep;
|
||
}
|
||
```
|
||
|
||
|
||
### JavaScript:
|
||
|
||
递归法:
|
||
|
||
```javascript
|
||
/**
|
||
* @param {TreeNode} root
|
||
* @return {number}
|
||
*/
|
||
var minDepth1 = function(root) {
|
||
if(!root) return 0;
|
||
// 到叶子节点 返回 1
|
||
if(!root.left && !root.right) return 1;
|
||
// 只有右节点时 递归右节点
|
||
if(!root.left) return 1 + minDepth(root.right);
|
||
// 只有左节点时 递归左节点
|
||
if(!root.right) return 1 + minDepth(root.left);
|
||
return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
|
||
};
|
||
```
|
||
|
||
迭代法:
|
||
|
||
```javascript
|
||
/**
|
||
* @param {TreeNode} root
|
||
* @return {number}
|
||
*/
|
||
var minDepth = function(root) {
|
||
if(!root) return 0;
|
||
const queue = [root];
|
||
let dep = 0;
|
||
while(true) {
|
||
let size = queue.length;
|
||
dep++;
|
||
while(size--){
|
||
const node = queue.shift();
|
||
// 到第一个叶子节点 返回 当前深度
|
||
if(!node.left && !node.right) return dep;
|
||
node.left && queue.push(node.left);
|
||
node.right && queue.push(node.right);
|
||
}
|
||
}
|
||
};
|
||
```
|
||
|
||
### TypeScript:
|
||
|
||
> 递归法
|
||
|
||
```typescript
|
||
function minDepth(root: TreeNode | null): number {
|
||
if (root === null) return 0;
|
||
if (root.left !== null && root.right === null) {
|
||
return 1 + minDepth(root.left);
|
||
}
|
||
if (root.left === null && root.right !== null) {
|
||
return 1 + minDepth(root.right);
|
||
}
|
||
return 1 + Math.min(minDepth(root.left), minDepth(root.right));
|
||
}
|
||
```
|
||
|
||
> 迭代法
|
||
|
||
```typescript
|
||
function minDepth(root: TreeNode | null): number {
|
||
let helperQueue: TreeNode[] = [];
|
||
let resMin: number = 0;
|
||
let tempNode: TreeNode;
|
||
if (root !== null) helperQueue.push(root);
|
||
while (helperQueue.length > 0) {
|
||
resMin++;
|
||
for (let i = 0, length = helperQueue.length; i < length; i++) {
|
||
tempNode = helperQueue.shift()!;
|
||
if (tempNode.left === null && tempNode.right === null) return resMin;
|
||
if (tempNode.left !== null) helperQueue.push(tempNode.left);
|
||
if (tempNode.right !== null) helperQueue.push(tempNode.right);
|
||
}
|
||
}
|
||
return resMin;
|
||
};
|
||
```
|
||
|
||
### Swift:
|
||
|
||
> 递归
|
||
```Swift
|
||
func minDepth(_ root: TreeNode?) -> Int {
|
||
guard let root = root else {
|
||
return 0
|
||
}
|
||
if root.left == nil && root.right != nil {
|
||
return 1 + minDepth(root.right)
|
||
}
|
||
if root.left != nil && root.right == nil {
|
||
return 1 + minDepth(root.left)
|
||
}
|
||
return 1 + min(minDepth(root.left), minDepth(root.right))
|
||
}
|
||
```
|
||
|
||
> 迭代
|
||
```Swift
|
||
func minDepth(_ root: TreeNode?) -> Int {
|
||
guard let root = root else {
|
||
return 0
|
||
}
|
||
var res = 0
|
||
var queue = [TreeNode]()
|
||
queue.append(root)
|
||
while !queue.isEmpty {
|
||
res += 1
|
||
for _ in 0 ..< queue.count {
|
||
let node = queue.removeFirst()
|
||
if node.left == nil && node.right == nil {
|
||
return res
|
||
}
|
||
if let left = node.left {
|
||
queue.append(left)
|
||
}
|
||
if let right = node.right {
|
||
queue.append(right)
|
||
}
|
||
}
|
||
}
|
||
return res
|
||
}
|
||
```
|
||
|
||
|
||
### Scala:
|
||
|
||
递归法:
|
||
```scala
|
||
object Solution {
|
||
def minDepth(root: TreeNode): Int = {
|
||
if (root == null) return 0
|
||
if (root.left == null && root.right != null) return 1 + minDepth(root.right)
|
||
if (root.left != null && root.right == null) return 1 + minDepth(root.left)
|
||
// 如果两侧都不为空,则取最小值,return关键字可以省略
|
||
1 + math.min(minDepth(root.left), minDepth(root.right))
|
||
}
|
||
}
|
||
```
|
||
|
||
迭代法:
|
||
```scala
|
||
object Solution {
|
||
import scala.collection.mutable
|
||
def minDepth(root: TreeNode): Int = {
|
||
if (root == null) return 0
|
||
var depth = 0
|
||
val queue = mutable.Queue[TreeNode]()
|
||
queue.enqueue(root)
|
||
while (!queue.isEmpty) {
|
||
depth += 1
|
||
val len = queue.size
|
||
for (i <- 0 until len) {
|
||
val curNode = queue.dequeue()
|
||
if (curNode.left != null) queue.enqueue(curNode.left)
|
||
if (curNode.right != null) queue.enqueue(curNode.right)
|
||
if (curNode.left == null && curNode.right == null) return depth
|
||
}
|
||
}
|
||
depth
|
||
}
|
||
}
|
||
```
|
||
|
||
### Rust:
|
||
|
||
```rust
|
||
impl Solution {
|
||
// 递归
|
||
pub fn min_depth(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {
|
||
if let Some(node) = root {
|
||
match (node.borrow().left.clone(), node.borrow().right.clone()) {
|
||
(Some(n1), None) => 1 + Self::min_depth(Some(n1)),
|
||
(None, Some(n2)) => 1 + Self::min_depth(Some(n2)),
|
||
(Some(n1), Some(n2)) => {
|
||
1 + std::cmp::min(Self::min_depth(Some(n1)), Self::min_depth(Some(n2)))
|
||
}
|
||
_ => 1,
|
||
}
|
||
} else {
|
||
0
|
||
}
|
||
}
|
||
|
||
// 迭代
|
||
// 需要 use std::collections::VecDeque;
|
||
pub fn min_depth(root: Option<Rc<RefCell<TreeNode>>>) -> i32 {
|
||
let mut res = 0;
|
||
let mut queue = VecDeque::new();
|
||
if root.is_some() {
|
||
queue.push_back(root);
|
||
}
|
||
while !queue.is_empty() {
|
||
res += 1;
|
||
for _ in 0..queue.len() {
|
||
let node = queue.pop_front().unwrap().unwrap();
|
||
if node.borrow().left.is_none() && node.borrow().right.is_none() {
|
||
return res;
|
||
}
|
||
if node.borrow().left.is_some() {
|
||
queue.push_back(node.borrow().left.clone());
|
||
}
|
||
if node.borrow().right.is_some() {
|
||
queue.push_back(node.borrow().right.clone());
|
||
}
|
||
}
|
||
}
|
||
res
|
||
}
|
||
}
|
||
```
|
||
|
||
<p align="center">
|
||
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
|
||
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
|
||
</a>
|