面试经典算法系列之二叉树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.hqwc.cn/news/631097.html

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

相关文章

227基于matlab的作业调度问题

基于matlab的作业调度问题。采用遗传算法&#xff0c;解决作业调度问题。一共三个作业&#xff0c;每个作业有不同的时间长度和紧急程度&#xff0c;超过时间会有惩罚措施。通过遗传算法计算出最好的作业安排&#xff0c;使得惩罚最小&#xff0c;获益最大。最终结果通过GUI用甘…

DevOps(七)Jenkins发布第一个流水线任务

Jenkins的流水线&#xff08;Pipeline&#xff09;是一种强大的工具&#xff0c;用于定义和管理持续集成和持续交付&#xff08;CI/CD&#xff09;过程。它允许你以代码的形式&#xff08;即"Pipeline as Code"&#xff09;定义整个构建、测试和部署流程&#xff0c;…

SQL --索引

索引 INDEX 伪列 伪装起来的列&#xff0c;不容易被看见&#xff0c;要特意查询才能看见 ROWNUM&#xff1a; 是对查询结果自动生成的一组连续的自然数序号。 SELECT emp.*,ROWNUM FROM emp例题&#xff1a;查询emp表中&#xff0c;前三个员工 SELECT * FROM * from emp w…

source map 开发优化工具

什么是 Source map 简单来说 Source map 就是一个存储信息的文件&#xff0c;里面储存着位置信息。 Source map 英文释义&#xff1a;源程序映射。 位置信息&#xff1a;转换后的代码 对应的 转换前的代码 位置映射关系。 有了 Source map&#xff0c;就算线上运行的是转换…

【GoWeb框架初探————Gin篇】

1. Gin 1.1 下载相应依赖 创建go项目&#xff0c;在项目下建立go.mod文件&#xff08;若有则跳过&#xff09; 命令行运行 go get github.com/gin-gonic/gin1.2 启动一个简单Web服务 package mainimport ("github.com/gin-gonic/gin""github.com/thinkerou/…

IDEA使用SCALA

一、在IDEA中下载插件 在设置->插件中找到scala&#xff0c;并下载。 下载完成后重启idea 二、在idea中创建spark的RDD操作项目 新建项目选中Scala。 创建完成后为项目添加java包&#xff0c;这个添加的是spark安装包中jars目录下的所有jar包 然后编写RDD操作 import or…

汽车视频智能剪辑解决方案,满足用户对高品质汽车视频的追求

随着汽车智能化和互联网技术的快速发展&#xff0c;车载视频已经成为现代驾驶生活不可或缺的一部分。然而面对海量的行车视频&#xff0c;如何高效地剪辑、整理并分享这些精彩瞬间&#xff0c;一直是车主和汽车内容创作者们所面临的难题。美摄科技&#xff0c;作为领先的视频智…

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

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

【行为型模式】模板方法模式

一、模板方法模式概述 模板方法模式定义&#xff1a;在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。(类对象型模式) 模板方法中的基本方法是实现算法的各个步骤&#xff0c;是模板方法的…

展览展会媒体媒体邀约执行应该怎么做?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 展览展会邀请媒体跟其他活动邀请媒体流程大致相同&#xff0c;包括 制定媒体邀约计划&#xff0c;准备新闻稿&#xff0c;发送邀请函&#xff0c;确认媒体参会&#xff0c;现场媒体接待及…

Linux的图形资源及指令

一、火车 1.切换到超级用户 su 2.下载资源 yum install -y sl 3.输入指令 sl&#xff0c;得到火车图形 如果没有得到该图形&#xff0c;就将2处改为yum install -y epel-release。 二、Linux的logo 1.在超级用户模式下下载资源 yum install -y linux_logo 2.输…

算法练习第20天|回溯算法 77.组合问题 257. 二叉树的所有路径

1.什么是回溯算法&#xff1f; 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。其本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出我们想要的答案。 2.为什么要有回溯算法? 那么既然回溯法并不高效为什么还要用它呢&#xff1f; 因为有的问题能暴力…