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

news2024/12/23 9:30:33

面试经典算法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 = val
        self.left = left
        self.right = right

class 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  # 如果当前节点值小于等于前一个节点的值,说明不是二叉搜索树,返回 False

            prev = 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.coloradmin.cn/o/1606281.html

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

相关文章

浅写个登录(无js文件)

全部代码如下&#xff0c;无需编写wxss文件&#xff0c;渲染都在style里面&#xff1a; <view style"height: 250rpx;width: 100%;"> <!-- 背景图片 --><view style"position: absolute; background-color: antiquewhite; height: 250rpx;width…

OpenHarmony UI动画-recyclerview_animators

简介 带有添加删除动画效果以及整体动画效果的list组件库 下载安装 ohpm install ohos/recyclerview-animatorsOpenHarmony ohpm 环境配置等更多内容&#xff0c;请参考如何安装OpenHarmony ohpm 包 使用说明 引入组件库 import { RecyclerView } from "ohos/recycler…

RabbitMQ项目实战(一)

文章目录 RabbitMQ项目实战选择客户端基础实战 前情提要&#xff1a;我们了解了消息队列&#xff0c;RabbitMQ的入门&#xff0c;交换机&#xff0c;以及核心特性等知识&#xff0c;现在终于来到了激动人心的项目实战环节&#xff01;本小节主要介绍通过Spring Boot RabbitMQ S…

UI5 快速入门教程

环境准备 node >16.8 ,VSCode&#xff0c;官方网址 开始 创建一个根文件夹&#xff0c;根文件中创建一个package.json文件 {"name": "quickstart-tutorial","private": true,"version": "1.0.0","author":…

吴恩达<用于LLM应用程序开发的LangChain> L1-Model_prompt_parser

问题预览/关键词 课程地址如何获取openAI的API Key如何根据日期设置不同模型?如何调用OpenAI的API?如何使用OpenAI的API&#xff1f;langchain如何抽象OpenAI的API接口&#xff1f;langchain如何创建提示词模板并查看模板内容&#xff1f;langchain如何使用提示词模板生成提…

【opencv】dnn示例-vit_tracker.cpp 使用OpenCV库和ViTTrack模型实现的视频追踪程序

这段代码是一个使用OpenCV库和ViTTrack模型实现的视频追踪程序。程序通过摄像头或视频文件获取图像序列&#xff0c;并对选定的目标对象进行实时追踪。 代码主要分为以下几个部分&#xff1a; 导入必要的库&#xff1a;程序开始时先导入了iostream&#xff0c;cmath以及相关Ope…

每日两题 / 53. 最大子数组和 56. 合并区间(LeetCode热题100)

53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; 经典dp题&#xff0c;dp[i]表示以nums[i]为结尾的所有子数组中&#xff0c;最大的和 将i从左到右遍历&#xff0c;考虑dp[i]如何维护&#xff1f; 以nums[i]结尾的子数组只有两种情况&#xff0c;子数组只有nums[i]…

[Qt网络编程]之UDP通讯的简单编程实现

hello&#xff01;欢迎大家来到我的Qt学习系列之网络编程之UDP通讯的简单编程实现。希望这篇文章能对你有所帮助&#xff01;&#xff01;&#xff01; 本篇文章的相关知识请看我的上篇文章: http://t.csdnimg.cn/UKyeM 目录 UDP通讯 基于主窗口的实现 基于线程的实现 UDP通讯…

小黄脸404自动跳转源码

源码介绍 小黄脸404自动跳转源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 效果截图 源码下载 小黄脸404自动跳转…

网络防火墙技术知多少?了解如何保护您的网络安全

在当前以网络为核心的世界中&#xff0c;网络安全成为了至关重要的议题。网络防火墙是一种常见的保护网络安全的技术&#xff0c;用于监控和控制网络流量&#xff0c;阻止未经授权的访问和恶意活动。今天德迅云安全就带您了解下防火墙的一些相关功能和类型。 防火墙的五个功能…

【EI会议征稿通知】2024年图像处理、机器学习与模式识别国际学术会议(IPMLP 2024)

2024年图像处理、机器学习与模式识别国际学术会议&#xff08;IPMLP 2024) 2024 International Conference on Image Processing, Machine Learning and Pattern Recognition 重要信息 大会官网&#xff1a;www.ipmlp.net&#xff08;点击参会/投稿/了解会议详情&#xff09;…

【操作系统】(Operator System)

目录 1.概念2.系统调用和库函数 1.概念 1.操作系统是什么呢&#xff1f;&#xff1f; 操作系统是一款进行软硬件资源管理的软件&#xff0c;我们打开电脑第一个加载的软件就是操作系统。 2.为什么要有操作系统呢&#xff1f; 操作系统通过将软硬件资源管理好(手段)&#xf…

axios的封装理解和基本使用

axios的配置 ruoyi的前端对axios进行了封装&#xff0c;让我们发get请求或者是post请求更加方便了。 ruoyi对axios的封装在下面文件中&#xff1a;打开文件&#xff0c;可以看到它有三个显眼的方法&#xff0c;分别是request拦截器、response拦截器和通用下载方法。ruoYi接口地…

ansible进阶-剧本调试方法

目录 1、调试方法 2、实例 2.1 Debug模块 2.2 tags标签 2.3 忽略错误 1、调试方法 具体方法应用场景debug标签执⾏剧本的时候输出剧本的执⾏流程,⼀般配合register⼀起使⽤. 输出facts变量自定义变量tags标签给⼀些模块加上标签,运⾏剧本的时候可以运⾏指定标签的…

基于非线性控制策略的电力电子电路——DC-DC电路的3种滑模控制器【MATLAB/simulink】

第一种&#xff0c;滞环滑模控制器Buck电路 在滑模控制系统中&#xff0c;采用滞环技术&#xff0c;直接将切换函数转换成开关控制信号&#xff0c;滞环技术被看做一种降低系统结构的切换频率的调制方法&#xff0c;业界也把基于滞环滑模技术实现的滑模控制称为直接滑模控制技…

【ESP32 手机配网教程】

【ESP32 手机配网教程】 1. 前言2. 先决条件2.1 环境配置2.2 所需零件3.3 硬件连接步骤 3. Web热点手动配网3.1. 准备工作3.2. 编译上传程序3.3. 进行手动配网 4. BLE无线配网4.1. 准备工作**4.2. 编译上传程序4.3. 使用手机APP进行无线配网 5. 总结 1. 前言 欢迎使用ESP32进行…

【C++】list的介绍及使用说明

目录 00.引言 01.list的介绍 模版类 独立节点存储 list的使用 1.构造函数 2.迭代器的使用 分类 运用 3.容量管理 empty()&#xff1a; size(): 4.元素访问 5.增删查改 00.引言 我们学习数据结构时&#xff0c;学过两个基本的数据结构&#xff1a;顺序表和链表。顺…

基于simulink的模拟锁相环和数字锁相环建模与对比仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 模拟锁相环&#xff08;PLL&#xff09;的基本原理 4.2 数字锁相环&#xff08;DPLL&#xff09;的基本原理 5.完整工程文件 1.课题概述 模拟锁相环和数字锁相环建模的simulink建模&#xff0c;对…

深度剖析哈希

目录 一. 哈希概念 1.1 哈希概念 1.2 哈希冲突 1.3 哈希函数 二. 闭散列 2.1 线性探测法 2.2 引进状态 2.3 闭散列的查找、插入、删除操作 2.4 闭散列插入时的扩容 2.4 仿函数 2.5 整体代码 三. 开散列 ​编辑 2.1 闭散列节点定义 2…

HBuilder真机调试检测不到荣耀Magic UI系列(包括手机和电脑)解决办法

HBuilder真机调试检测不到荣耀Magic UI系列&#xff08;包括手机和电脑&#xff09;解决办法解决方法&#xff1a; 1.在开发人员选项中开启USB调试 如何进入开发者选项&#xff1f; 设置->关于->版本号&#xff0c;点击版本号直至出现您已处于开发者模式 2.选择USB配置…