面试经典算法系列之二叉树17 -- 验证二叉树

面试经典算法32 - 验证二叉树

LeetCode.98
公众号:阿Q技术站

问题描述

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:root = [2,1,3]
输出:true

示例 2:

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

提示:

  • 树中节点数目范围在[1, 104]
  • -231 <= Node.val <= 231 - 1

思路

为了判断一个二叉树是否是有效的二叉搜索树,可以利用二叉搜索树的性质:对于每个节点,其左子树的所有节点都小于当前节点,右子树的所有节点都大于当前节点。同时,左右子树也必须分别是有效的二叉搜索树。

递归
  1. 定义一个辅助函数 isValidBSTHelper,用于递归判断以当前节点为根的子树是否是有效的二叉搜索树。
  2. 在辅助函数中,传入当前节点、允许的最小值和最大值。
  3. 如果当前节点为空,说明是有效的二叉搜索树,返回 true。
  4. 如果当前节点的值不在最小值和最大值的范围内,说明不是有效的二叉搜索树,返回 false。
  5. 递归判断左子树和右子树,左子树的最大值为当前节点的值,右子树的最小值为当前节点的值。
  6. 如果左子树和右子树都是有效的二叉搜索树,则当前节点也是有效的二叉搜索树,返回 true。
非递归
  1. 使用栈来模拟中序遍历的过程,从根节点开始,先将左子节点依次入栈,直到最左下的叶子节点。
  2. 弹出栈顶节点,判断其值是否大于前一个节点的值(如果存在)。如果不满足递增关系,则不是有效的二叉搜索树,返回 false。
  3. 将当前节点的右子节点入栈,继续遍历右子树。
  4. 重复步骤 2 和步骤 3,直到栈为空且所有节点都遍历完毕。

图解

  1. 创建空栈 s 和当前节点指针 curr,初始化 prevlong 类型最小值

  1. 如果当前节点不为空或栈不为空,则将当前节点入栈,然后将当前节点指向其左子节点,重复此步骤直到当前节点为空。

  1. 如果当前节点为空,说明已经到达最左下角的节点,将栈顶节点弹出。

  1. 栈顶元素的值不小于long类型的最小值。将 prev 更新为当前节点值。

  1. 处理右子节点。

此时,当前节点值小于前一个节点的值,说明不是二叉搜索树,返回 false。

参考代码

C++
递归
#include <iostream>
#include <limits>using namespace std;// 二叉树节点的定义
struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};class Solution {
public:bool isValidBST(TreeNode* root) {return isValidBSTHelper(root, numeric_limits<long>::min(), numeric_limits<long>::max());}private:bool isValidBSTHelper(TreeNode* root, long minVal, long maxVal) {if (!root) {return true; // 空节点是有效的二叉搜索树}if (root->val <= minVal || root->val >= maxVal) {return false; // 当前节点的值不在允许的范围内,不是有效的二叉搜索树}// 递归判断左子树和右子树return isValidBSTHelper(root->left, minVal, root->val) && isValidBSTHelper(root->right, root->val, maxVal);}
};// 创建二叉树
TreeNode* createTree(vector<int>& nodes, int index) {if (index >= nodes.size() || nodes[index] == -1) {return nullptr; // 如果节点为空,则返回nullptr}TreeNode* root = new TreeNode(nodes[index]); // 创建当前节点root->left = createTree(nodes, 2 * index + 1); // 创建左子树root->right = createTree(nodes, 2 * index + 2); // 创建右子树return root; // 返回当前节点
}int main() {vector<int> nodes = {2, 1, 3}; // 二叉搜索树的中序遍历序列TreeNode* root = createTree(nodes, 0); // 创建二叉树Solution solution;bool result = solution.isValidBST(root); // 判断二叉树是否是有效的二叉搜索树cout << (result ? "true" : "false") << endl; // 输出结果return 0;
}
非递归
#include <iostream>
#include <stack>
#include <limits>using namespace std;// 二叉树节点的定义
struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};class Solution {
public:bool isValidBST(TreeNode* root) {stack<TreeNode*> s; // 辅助栈,用于模拟中序遍历过程TreeNode* curr = root; // 当前节点long prev = numeric_limits<long>::min(); // 用于保存前一个节点的值,初始化为 long 类型最小值while (curr || !s.empty()) {while (curr) {s.push(curr); // 将当前节点入栈curr = curr->left; // 遍历左子树}curr = s.top(); // 获取栈顶节点s.pop(); // 弹出栈顶节点if (curr->val <= prev) {return false; // 如果当前节点值小于等于前一个节点的值,说明不是二叉搜索树,返回 false}prev = curr->val; // 更新前一个节点的值为当前节点的值curr = curr->right; // 处理右子节点}return true; // 遍历完所有节点,返回 true}
};// 创建二叉树
TreeNode* createTree(vector<int>& nodes, int index) {if (index >= nodes.size() || nodes[index] == -1) {return nullptr; // 如果节点为空,则返回nullptr}TreeNode* root = new TreeNode(nodes[index]); // 创建当前节点root->left = createTree(nodes, 2 * index + 1); // 创建左子树root->right = createTree(nodes, 2 * index + 2); // 创建右子树return root; // 返回当前节点
}int main() {vector<int> nodes = {2, 1, 3}; // 二叉搜索树的中序遍历序列TreeNode* root = createTree(nodes, 0); // 创建二叉树Solution solution;bool result = solution.isValidBST(root); // 判断二叉树是否是有效的二叉搜索树cout << (result ? "true" : "false") << endl; // 输出结果return 0;
}
Java
import java.util.Stack;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}class Solution {public boolean isValidBST(TreeNode root) {Stack<TreeNode> stack = new Stack<>(); // 辅助栈,用于模拟中序遍历过程TreeNode curr = root; // 当前节点long prev = Long.MIN_VALUE; // 用于保存前一个节点的值,初始化为 long 类型最小值while (curr != null || !stack.isEmpty()) {while (curr != null) {stack.push(curr); // 将当前节点入栈curr = curr.left; // 遍历左子树}curr = stack.pop(); // 获取栈顶节点if (curr.val <= prev) {return false; // 如果当前节点值小于等于前一个节点的值,说明不是二叉搜索树,返回 false}prev = curr.val; // 更新前一个节点的值为当前节点的值curr = curr.right; // 处理右子节点}return true; // 遍历完所有节点,返回 true}
}public class Main {public static void main(String[] args) {TreeNode root = new TreeNode(2);root.left = new TreeNode(1);root.right = new TreeNode(3);Solution solution = new Solution();boolean result = solution.isValidBST(root); // 判断二叉树是否是有效的二叉搜索树System.out.println(result); // 输出结果}
}
Python
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightclass Solution:def isValidBST(self, root: TreeNode) -> bool:stack = []  # 辅助栈,用于模拟中序遍历过程curr = root  # 当前节点prev = float("-inf")  # 用于保存前一个节点的值,初始化为负无穷while curr or stack:while curr:stack.append(curr)  # 将当前节点入栈curr = curr.left  # 遍历左子树curr = stack.pop()  # 获取栈顶节点if curr.val <= prev:return False  # 如果当前节点值小于等于前一个节点的值,说明不是二叉搜索树,返回 Falseprev = curr.val  # 更新前一个节点的值为当前节点的值curr = curr.right  # 处理右子节点return True  # 遍历完所有节点,返回 True# 创建二叉树
root = TreeNode(2)
root.left = TreeNode(1)
root.right = TreeNode(3)solution = Solution()
result = solution.isValidBST(root)  # 判断二叉树是否是有效的二叉搜索树
print(result)  # 输出结果

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

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

相关文章

ActiveMQ 如果数据处理出现异常会怎么样

我们有一个 Spring 的客户端&#xff0c;在处理消息的时候因为程序的原因出现消息处理异常。 对这种情况&#xff0c;ActiveMQ 会把出现异常的消息放在 DLQ 队列中进行持久化。 因此&#xff0c;在 ActiveMQ 消息处理队列中需要持续关注 DLQ 队列&#xff0c; DLQ 的队列都是无…

生成人工智能体:人类行为的交互式模拟论文与源码架构解析(5)——可控评估端到端评估

最后完结篇,文末有测试中发现的有趣现象,并附上了相关资料链接~ 5.可控评估 分两个阶段评估生成代理。我们从一个更加严格控制的评估开始,单独评估代理的响应,以了解它们是否在狭义上定义的上下文中产生可信的行为。然后,在我们对代理社区进行为期两天的端到端分析中,我…

蓝桥杯2024年第十五届省赛真题-数字接龙

思路&#xff1a;DFS&#xff0c;因为输入的i&#xff0c;j的顺序导致&#xff0c;方向向量中x是行编号&#xff0c;y是列编号。方向向量可能和直觉上不同。 错的 //int dx[8]{0,1,1,1,0,-1,-1,-1}; //int dy[8]{1,1,0,-1,-1,-1,0,1}; 对的 int dx[]{-1,-1,0,1,1,1,0,-1}; int…

数据结构(顺序栈

目录 1. 讲解&#xff1a;2. C代码实现&#xff1a;小结&#xff1a; 1. 讲解&#xff1a; 用顺序的物理结构&#xff08;数组&#xff09;存储栈这个数据结构&#xff0c;实现栈的创建、销毁、增删查、判空。 top指针的指向位置有两种实现方法&#xff1a;一个是指向栈顶元素…

页缓存(PageCache)和预读机制(readahead )

页缓存&#xff08;PageCache)和预读机制&#xff08;readahead &#xff09; 页缓存&#xff08;PageCache)是操作系统&#xff08;OS&#xff09;对文件的缓存&#xff0c;用于加速对文件的读写。 page 是内存管理分配的基本单位&#xff0c; Page Cache 由多个 page 构成&…

《Super Simple Skybox》天空盒 -- 创造绝美天空的神奇工具!限时免费!

《Super Simple Skybox》天空盒 -- 创造绝美天空的神奇工具&#xff01;限时免费&#xff01; 前言内容介绍资源特色动态&#xff0c;美丽的天空在几秒钟内即插即用 功能列表领取兑换码 前言 ^^在这个充满创意与想象的世界里&#xff0c;Unity 免费资源犹如一颗璀璨的明珠&…

react合成事件与原生事件区别备忘

朋友问起在做一个下拉框组件&#xff0c;下拉的点击事件是用react的onClick触发&#xff0c;外部区域点击关闭则用dom的原生点击事件绑定&#xff0c;问题是下拉的点击事件无法阻止冒泡到dom的原生事件。 我说&#xff0c;react的合成事件 和 原生事件是不一样的&#xff0c;尽…

三、fpga对完成过滤和校验的有效包数据进行有效像素提取、MATLAB对数据源进行处理与下发(完整实现pc机→显示器通信链路)

前言:上篇文章实现了MATLAB模拟发送UDP以太网协议数据包到fpga,能实现双沿数据→单沿数据转换,并将转换后的数据进行包过滤和crc校验,本篇内容要实现真正的从pc机下发视频数据,经过千兆以太网传输存储到fpga 的ddr3中,然后通过hdmi读出到显示屏上。 文章目录 一、模块设…

鸿蒙入门06-常见装饰器( 简单装饰器 )

装饰器是鸿蒙开发中非常重要的一个环节因为在很多地方我们都需要用到装饰器并且如果我们想高度的复用, 那么装饰器就是必不可少的一环接下来我们就来介绍一些常见的装饰器注意 : 所有装饰器首字母大写 Entry 用来装饰 struct 使用表示页面的入口 Component 装饰 struct, …

基于Springboot的论坛管理系统

基于SpringbootVue的论坛管理系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 公告 热门帖子 论坛新天地 新闻资讯 留言反馈 后台登录 用户管理 公告管理…

java宠物领养系统的设计与实现(springboot+mysql+源码)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的宠物领养系统的设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于Spring Bo…

JavaWeb--04YApi,Vue-cli脚手架Node.js环境搭建,创建第一个Vue项目

04 1 Yapi2 Vue-cli脚手架Node.js环境搭建配置npm的全局安装路径 3 创建项目&#xff08;这个看下一篇文章吧&#xff09; 1 Yapi 前后端分离中的重要枢纽"接口文档",以下一款为Yapi的接口文档 介绍&#xff1a;YApi 是高效、易用、功能强大的 api 管理平台&#…

Springboot引入外部jar包并打包jar包

前言 spring boot项目开发过程中难免需要引入外部jar包&#xff0c;下面将以idea为例说明操作步骤 将需要的jar包导入到项目中 2.在maven中引入jar包 <dependency><groupId>com</groupId><!--随便填的文件夹名称--><artifactId>xxx</artif…

linux内核初始化成功后是如何过渡到android初始化的

Android用的linux内核&#xff0c;以完成OS该有的功能&#xff0c;例如&#xff0c;文件系统&#xff0c;网络&#xff0c;内存管理&#xff0c;进程调度&#xff0c;驱动等 &#xff0c;向下管理硬件资源向上提供系统调用。另一些Android特有驱动也放在内核之中。 当linux内核…

【Qt】Qt安装包、源码、子模块(submodules)下载

1、Qt 4.0 ~ Qt5.14 Qt 4.0 ~ Qt5.14 离线安装包、源码和子模块(submodules)源码下载路径: https://download.qt.io/new_archive/qt/以Qt5.7.1为例,注意子模块都是源码,需要独立编译 2、Qt5.15 ~ Qt6.7 Qt5.15 ~ Qt6.7源码和子模块(submodules)源码下载路径: htt…

LabVIEW供热管道泄漏监测与定位

LabVIEW供热管道泄漏监测与定位 在现代城市的基础设施建设中&#xff0c;供热管道系统起着极其重要的作用。然而&#xff0c;管道泄漏问题不仅导致巨大的经济损失&#xff0c;还对公共安全构成威胁。因此&#xff0c;开发一种高效、准确的管道泄漏监测与定位技术显得尤为关键。…

GUI02-在窗口上跟踪并输出鼠标位置(Win32版)

(1) 响应 WM_MOUSEMOVE 消息获得鼠标位置&#xff1b; (2) 响应 WM_PAINT 将鼠标位置输出到窗口中&#xff1b; (3) 学习二者之间的关键步骤&#xff1a;调用 InvalidateRect() 以通知窗口重绘。 零. 课堂视频 在窗口上跟踪输出鼠标位置-Win32版 一、关键知识点 1. BeginPaint…

HANA SQL消耗内存和CPU线程的限制参数

HANA再处理大数据表相关的复杂Sql时&#xff0c;如果没有设置Memory和CPU线程上限的话&#xff0c;会将HANA的资源占用殆尽&#xff0c;造成HANA无法响应其他Sql请求&#xff0c;导致表现在应用服务器上就是系统卡顿的情况。解决上述问题的办法就是按照下图设置Memory(图1&…

穿越物联网的迷雾:深入理解MQTT协议

目录标题 1、MQTT简介核心特性 2、MQTT的工作原理通信过程 3、MQTT的消息质量&#xff08;QoS&#xff09;4、安全机制5、实践应用环境准备示例项目发布者客户端订阅者客户端 6、最佳实践7、结论8、参考资料 在物联网&#xff08;IoT&#xff09;的海洋中&#xff0c;数据像水流…

软考 - 系统架构设计师 - 设计模式

概念 每一个设计模式描述了一个在我们周围不断重复发生的问题&#xff0c;以及该问题解决方案的核心&#xff0c;这样&#xff0c;就可以在遇到相同的问题时使用该解决方案进行解决&#xff0c;不必进行重复的工作&#xff0c;设计模式的核心在于提供了问题的解决方案&#xff…