【Leetcode】111. 二叉树的最小深度

news2024/12/24 10:33:35

一、题目

1、题目描述

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

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

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

示例1:
在这里插入图片描述

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

示例2:

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

2、基础框架

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int minDepth(TreeNode* root) {

    }
};

3、原题链接

111. 二叉树的最小深度

二、解题报告

1、思路分析

利用Morris遍历的改写的要点:

【麻烦的地方】
Morris遍历有修改指针的情况

【需要解决的问题】

  1. cur来到哪个节点,正确地记录该节点所在的层level
  2. 正确地发现叶节点

【1、假设刚遍历过的X节点在第 L 层,现在遍历到Y节点,那么Y节点在第几层呢?】

如果Y节点是X的左孩子,则Y在第L + 1层;如果Y是X的右孩子,则Y不一定在L + 1层,因为可能是X修改后的指针指向Y。

所以这样判断:

  • 如果Y的左树最右节点不是X,则Y一定在第 L + 1 层;
  • 若Y左树的最右节点是X(一定是X的指针修改指向了Y),得到Y左树右边界的节点个数num,然后Y所在的层就是 L - num请添加图片描述

【2、如何发现所有的叶节点】

对于能两次访问到的节点,第二次访问结束后,恢复了指针指向后判断是否为叶节点。即不是到某一个节点判断它是否为叶子节点,而是根据上边回到一个节点的时候,反过来看有没有叶子节点。

Morris遍历结束后,最后还要看看整棵树的最右节点是不是叶节点。

【整个流程举例说明】
在这里插入图片描述
设置变量,一开始:之前遍历到的节点pre = a,所在层数level = 0,最小深度minH = 系统最大。

开始Morris遍历:

  • 来到 a 节点的时候,a 节点左树的最右节点是 f,f 不等于之前遍历到的节点pre = a,所以 a 在第 1 层;因为是第1次来到 a 节点,所以不去发现叶节点,时机未到,此时 f 右指针指向 a。【pre = a,level = 1, minH = 系统最大】
  • 接下来到 b 节点,b 左树最右节点是 k,k ≠ a,所以b一定在第 2 层;又是第1次来到b节点,所以不去发现叶节点,此时修改 k 的右指针指向 b。【pre = b,level = 2, minH = 系统最大】
  • 来到 k 节点,k 左树的最右节点为空,不等于 b,所以 k 一定在第3层;且k只能到自己1次,所以也不去发现任何叶节点。【pre = k,level = 3,minH = 系统最大】
  • 然后顺着k的右指针回到 b,b的左树最右节点k 等于上一个遍历到的节点pre = k,就知道是从底层上来到,修正 b 所在的层数:level - 左树右边界的长度 = 3 - 1 = 2;又因为是第2次回到b,恢复k的右指针指向空,又因为知道b所在的层数,所以能知道k所在的层数 = b的层数 + 1,且k是叶节点,所以minH = 3。【pre = b,level = 2, minH = 3】
  • 来到 c 节点,c 左树的最右节点为空,不等于b,所以 c 一定在第 3 层,且因为是第1次来到c,也不用去结算叶节点;【pre = c,level = 3, minH = 3】
  • 来到 f 节点,f 左树的最右节点 s 不等于 c,所以 f 一定在第 4 层,因为是第一次来到 f,不用去结算叶节点,修改 s 的右指针指向 f;【pre = f,level = 4, minH = 3】
  • 来到 t 节点,t 左树的最右节点为空,不等于 f,所以 t 一定在第 5 层,且是第一次来到 t,不用结算叶节点;【pre = t,level = 5, minH = 3】
  • 来到 s 节点,s 左树的最右节点为空,不等于 t,所以 s 一定在第 6 层,且是第一次来到 s,不用结算叶节点;【pre = s,level = 6, minH = 3】
  • 回到 f 节点,f 左树的最右节点 s,等于之前遍历的节点pre,知道是从底层上去的,修正 f 所在的层数 = level - 左树右边界节点数 = 6 - 2 = 4,是第二次来到 f 节点,恢复 s 的右指针指向空,且 s 为叶节点,根据 f 所在的层数算出 s 所在的层数 = f 所在层数+ 2=6,6 >minH = 3,所以minH保持3不变;【pre = f,level = 4, minH = 3】
  • 顺着 f 回到 a 节点,a 左树最右节点 f = pre,知道是从底层上去的,修正 a 所在的层数 = level - a左树右边界节 点数 = 4 - 3 = 1,是第二次来到 a 节点,恢复 f 的右指针指向空,而 f 不为叶节点,所以不用更新minH。【pre = a, level = 1,minH = 3】
  • 完成Morris遍历后,要看整棵树的最右节点是否为叶节点,如果为叶节点要结算出它的深度,与minH进行比较。

2、时间复杂度

O ( N ) O(N) O(N)

3、代码详解

  • 方法一:二叉树的递归套路
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == nullptr) return 0;

        return p(root);
    }
	
    //返回以x为头的树的最小深度
    //时间复杂度O(N),空间复杂度O(树的高度)
    int p(TreeNode *x) {
        //左右子树都为空,则为叶子节点
        if (x->left == nullptr && x->right == nullptr) {
            return 1;
        }
        
        //左右子树起码有一个不为空
        
        int leftH = INT_MAX;
        if (x->left != nullptr) {
            leftH = p(x->left); //得到左树的最小深度
        }

        int rightH = INT_MAX;
        if (x->right != nullptr) {
            rightH = p(x->right); //得到右树的最小深度
        }

        return 1 + min(leftH, rightH);
    }
};
  • 方法二:根据Morris遍历改写
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    //Morris遍历的解
    int minDepth(TreeNode* root) {
        if (root == nullptr) return 0;

        TreeNode *cur = root;
        TreeNode *mostRight = nullptr;
        int curLevel = 0;
        int minH = INT_MAX;
        while (cur != nullptr) {
            mostRight = cur->left;
            if (mostRight != nullptr) {
                int rightBoardSize = 1;
                while (mostRight->right != nullptr && mostRight->right != cur) {
                    rightBoardSize++;
                    mostRight = mostRight->right;
                }

                if (mostRight->right == nullptr) { //第一次到达cur
                    curLevel++;
                    mostRight->right = cur;
                    cur = cur->left;
                    continue;
                } else {//第二次到达cur
                    if (mostRight->left == nullptr) { //cur左树的最右节点为叶节点
                        minH = min(minH, curLevel);
                    }
                    curLevel -= rightBoardSize; //修正cur的层数
                    mostRight->right = nullptr;
                }
            } else { //只能到达一次
                curLevel++;
            }
            cur = cur->right;
        }

        //判断整棵树的最右节点是否为叶节点
        int finalRight = 1;
        cur = root;
        while (cur->right != nullptr) {
            finalRight++;
            cur = cur->right;
        }
        if (cur->left == nullptr && cur->right == nullptr) {
            minH = min(minH, finalRight);
        }

        return minH;
    }
};

两个方法的Java版代码:

// 本题测试链接 : https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
public class MinDepth {

	// 不提交这个类
	public static class TreeNode {
		public int val;
		public TreeNode left;
		public TreeNode right;

		public TreeNode(int x) {
			val = x;
		}
	}

	// 下面的方法是一般解
	public static int minDepth1(TreeNode head) {
		if (head == null) {
			return 0;
		}
		return p(head);
	}

	// 返回x为头的树,最小深度是多少
	public static int p(TreeNode x) {
		if (x.left == null && x.right == null) {
			return 1;
		}
		// 左右子树起码有一个不为空
		int leftH = Integer.MAX_VALUE;
		if (x.left != null) {
			leftH = p(x.left);
		}
		int rightH = Integer.MAX_VALUE;
		if (x.right != null) {
			rightH = p(x.right);
		}
		return 1 + Math.min(leftH, rightH);
	}

	// 下面的方法是morris遍历的解
	public static int minDepth2(TreeNode head) {
		if (head == null) {
			return 0;
		}
		TreeNode cur = head;
		TreeNode mostRight = null;
		int curLevel = 0;
		int minHeight = Integer.MAX_VALUE;
		while (cur != null) {
			mostRight = cur.left;
			if (mostRight != null) {
				int rightBoardSize = 1;
				while (mostRight.right != null && mostRight.right != cur) {
					rightBoardSize++;
					mostRight = mostRight.right;
				}
				if (mostRight.right == null) { // 第一次到达
					curLevel++;
					mostRight.right = cur;
					cur = cur.left;
					continue;
				} else { // 第二次到达
					if (mostRight.left == null) {
						minHeight = Math.min(minHeight, curLevel);
					}
					curLevel -= rightBoardSize;
					mostRight.right = null;
				}
			} else { // 只有一次到达
				curLevel++;
			}
			cur = cur.right;
		}
		int finalRight = 1;
		cur = head;
		while (cur.right != null) {
			finalRight++;
			cur = cur.right;
		}
		if (cur.left == null && cur.right == null) {
			minHeight = Math.min(minHeight, finalRight);
		}
		return minHeight;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/136313.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4.0、Linux-文件属性查看和修改学习

4.0、Linux-文件属性查看和修改学习 基本属性 Linux 系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限;为了保护系统的安全性,Linux 系统对不同的用户访问同一文件(包括目录文件)的权限…

Linux0基础入门,教你如何在Shell中使用正则表达式

正则表达式 在 shell 脚本中成功运用 sed 编辑器和 gawk 程序的关键在于熟练使用正则表达式。这可不是件简单的事,从大量数据中过滤出特定数据可能会(而且经常会)很复杂。本章将介绍如何在 sed 编辑器和 gawk 程序中创建正则表达式来过滤出需…

聚观早报 | 元旦机票预订量增长145%;小米集团副总裁崔宝秋离职

今日要闻:元旦跨境机票预订量增长145%;小米集团副总裁崔宝秋离职;抖音推出桌面端聊天软件;《阿凡达2》全球票房破14亿美元;苹果 A17 芯片要用 3nm 工艺元旦跨境机票预订量增长145% 1 月 2 日,各旅游平台发布…

小工具集锦,5款好用的良心软件

今天来给大家推荐5款良心软件,每款都是经过时间检验的精品,用起来让你的工作效率提升飞快,各个都让你觉得相见恨晚! 1.高效截图——Snipaste 我曾经尝试过 FastStone Capture、ShareX 等多款截图软件,直到遇见 Snipaste 才画上句点。除了基…

“当不存在跨域问题,也解决了数据验证时,还出现:No ‘Access-Control-Allow-Origin‘,说存在跨域问题 ”的解决办法

不存在跨域问题,数据验证也弄好了,还出现下面的问题:Access to XMLHttpRequest at https://m.maizuo.com/gateway?cityId440100&pageNum1&pageSize10&type1&k7325551 from origin http://localhost:8080 has been blocked b…

OpenCv:直方图均衡化(HE),自适应直方图均衡化(AHE),限制对比度自适应直方图均衡化(CLAHE)

总结了使用Python OpenCv处理图像直方图均衡化(HE),自适应直方图均衡化(AHE),限制对比度自适应直方图均衡化(CLAHE)的方法。 目录直方图均衡化(HE)自适应直方图均衡化(AHE)限制对比度自适应直方图均衡化(CLAHE)代码测试结果场景1场景2直方图均衡化(HE) …

MySQL 体系结构

MySQL经典体系结构 MySQL连接: MySQL Protocol(MySQL协议) Socket (Unix Scoket)本地连接 比如:mysql -uroot -p123 -S /tmp/mysql.sock TCP/IP(远程连接) 比如:mysql -uroot -p123 -h 10.0.0.51 -P3306 API (应用程序、开发) Native API C、PHP、JDBC、ODBC、.NET、Pyth…

大数据资源整合分析平台_企业大数据管理软件_大数据资源整合管理系统

在现如今大数据时代下,很多公司拥有大量的数据信息,这些数据繁多并且复杂,对于不懂技术的人员来讲,无疑是巨大的难题,这些数据背后存在着巨大的价值,我们可以利用大数据的方式,来提高数据的使用…

戴尔灵越14电脑U盘重装系统方法分享

戴尔灵越14电脑U盘重装系统方法分享。一些用户的戴尔灵越14电脑在进行了系统升级之后,出现了系统不兼容的情况,导致自己的电脑桌面出现了蓝屏的情况。那么这个情况下我们怎么去将系统进行重装呢?一起来看看U盘重装系统的方法吧。 准备工作&am…

详解前端页面性能测试方案——开源工具Lighthouse

相信绝大多数测试同学对于前端页面的性能测试都是通过使用各个浏览器的开发者工具进行抓包来查看响应中各种请求、js和图片的响应时间,然后把发现慢的点进行截图,然后汇总给开发人员。今天给大家介绍一款工具,不仅可以对页面性能进行打分&…

同步、异步、单工、双工、半双工有啥不同

1-同步、异步通信 串行通信通常情况下分为同步和异步通信,同步通信需要同步时钟信号,而异步通信则是不需要同步时钟信号的。 同步通信:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。 异步通信:…

物联网架构实例—Ubuntu 安装RabbitMQ

1.安装前准备 1.1.更新apt-get源 apt-get update 1.2.erlang支持 rabbitMq需要erlang语言的支持,在安装rabbitMq之前需要安装erlang. apt-get install erlang-nox 1.3.查看erlang版本 erl 1.4.添加公钥 wget -O- https://www.rabbitmq.com/rabbitmq-release-…

SQL常用语句

SQL(Structured Query Language) 结构化查询语言,是用来访问和处理数据库的编程语言 使用SQL语言编写出来的代码叫做SQL语句 SQL语言只能在关系型数据库(MySQL,Oracle,SQL Sever)中使用。非关系型数据库(Mongodb)不支持SQL语言 在SQL语句中的关键字无所谓大小写&…

C++泛型算法

泛型算法copy()源码:自实现myCopy()find()源码:自实现myFind()sort()源码:自实现mySort()2个参数3个参数find_if自实现myFind_if()copy() 作用:将某容器的区间数据拷贝到指定指定容器的指定位置 前两个参数是普通的顺序型迭代器…

多线程(java)

1.线程相关概念 1.1程序(program) 是为完成特定任务、用某种语言编写的一组指令的集合。 简单的说:就是我们写的代码 1.2 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷…

前端在线写代码——打造一个自己的编辑器

前端在线写代码——打造一个自己的编辑器前言html格式化css、js格式化List item ) 前言 下载IDE编辑器占用太大,第三方在线编辑空间又给太小,于是乎自己动手开发一个。功能有 1、html格式化 2、css格式化 3、js格式化 4、导入包 5、导出html网页 6、自…

lombok原理

1.更简单的⽇志输出—lombok 每次都使⽤ LoggerFactory.getLogger(xxx.class) 很繁琐,且每个类都添加⼀遍,也很麻烦,这⾥讲⼀种更好⽤的⽇志输出⽅式,使⽤ lombok 来更简单的输出。 添加 lombok 框架⽀持。使⽤ slf4j 注解输出⽇…

终于弄明白了 RocketMQ 的存储模型

RocketMQ 优异的性能表现,必然绕不开其优秀的存储模型 。 这篇文章,笔者按照自己的理解 , 尝试分析 RocketMQ 的存储模型,希望对大家有所启发。 1 整体概览 首先温习下 RocketMQ 架构。 整体架构中包含四种角色 : Producer :消息…

如何快速传输大文件:4 种大文件传输有效的方法

文件大小正在爆炸式增长,随之而来的挑战是如何仍然以快速、安全的方式发送。从这个意义上说,弄清楚如何快速传输大文件似乎是一项几乎不可能完成的任务。随着工作流程不断适应数字化,这对于自由职业者、业余视频编辑、后期制作公司和广播公司…

深挖产品护城河,鹿客科技打开成长天花板

2000-2010年,随着指纹识别技术开始应用于智能门锁,其产品应用领域也开始从酒店推广到普通家庭。2011年以来,随着人脸识别、生物识别技术的兴起,消费者对于产品的需求度不断提升,智能门锁行业也迎来一轮飞跃式发展。来源…