系列文章目录

算法----滑动窗口
算法----二叉树


文章目录

  • 系列文章目录
  • 二叉搜索树心法(特性篇)
  • 二叉搜索树心法(基操篇)
    • 1、判断 BST 的合法性
    • 2、在 BST 中搜索元素
    • 3、在 BST 中插入一个数
    • 4、在 BST 中删除一个数


二叉搜索树心法(特性篇)

BST 的特性:

  1. 对于 BST 的每一个节点 node,左子树节点的值都比 node 的值要小,右子树节点的值都比 node 的值大。

  2. 对于 BST 的每一个节点 node,它的左侧子树和右侧子树都是 BST。

从做算法题的角度来看 BST,除了它的定义,还有一个重要的性质:BST 的中序遍历结果是有序的(升序)

void traverse(TreeNode* root) {if (root == nullptr)  return;traverse(root->left);// 中序遍历(升序)print(root->val);traverse(root->right);
}

BST 的中序遍历代码可以升序打印节点的值,那如果我想降序打印节点的值怎么办?很简单,只要把递归顺序改一下,先遍历右子树,后遍历左子树,就能够降序遍历节点

void traverse(TreeNode* root) {if (root == nullptr)  return;traverse(root->right);// 中序遍历(降序)print(root->val);traverse(root->left);
}

二叉搜索树心法(基操篇)

1、判断 BST 的合法性

按照 BST 左小右大的特性,每个节点想要判断自己是否是合法的 BST 节点,要做的事好像是比较自己和左右孩子,比左孩子大,右孩子小。就会写出这样的代码:

bool isValidBST(TreeNode* root) {if (root == nullptr) return true;// root 的左边应该更小if (root->left != nullptr && root->left->val >= root->val)return false;// root 的右边应该更大if (root->right != nullptr && root->right->val <= root->val)return false;return isValidBST(root->left)&& isValidBST(root->right);
}

这样写出的代码是错误的,BST 的每个节点还要应该要小于右边子树的所有节点,下面这个二叉树显然不是 BST,因为节点 7 的左子树中有一个节点 8,但是上面的算法代码会把它判定为合法 BST:
在这里插入图片描述
错误的原因在于,对于每一个节点 root,代码值检查了它的左右孩子节点是否符合左小右大的原则;但是根据 BST 的定义,root 的整个左子树都要小于 root.val,整个右子树都要大于 root.val

问题是,对于某一个节点 root,他只能管得了自己的左右子节点,怎么把 root 的约束传递给左右子树呢?请看正确的代码:

class Solution {
public:bool isValidBST(TreeNode* root) {return _isValidBST(root, nullptr, nullptr);}// 定义:该函数返回 root 为根的子树的所有节点是否满足 max->val > root->val > min->valbool _isValidBST(TreeNode* root, TreeNode* min, TreeNode* max) {// base caseif (root == nullptr) return true;// 若 root->val 不符合 max 和 min 的限制,说明不是合法 BSTif (min != nullptr && root->val <= min->val) return false;if (max != nullptr && root->val >= max->val) return false;// 根据定义,限定左子树的最大值是 root->val,右子树的最小值是 root->valreturn _isValidBST(root->left, min, root) && _isValidBST(root->right, root, max);}
};

2、在 BST 中搜索元素

// 定义:在以 root 为根的 BST 中搜索值为 target 的节点,返回该节点
TreeNode* searchBST(TreeNode* root, int target) {if (root == nullptr) {return nullptr;}// 去左子树搜索if (root->val > target) {return searchBST(root->left, target);}// 去右子树搜索if (root->val < target) {return searchBST(root->right, target);}// 当前节点就是目标值return root;
}

3、在 BST 中插入一个数

// 定义:在以 root 为根的 BST 中插入 val 节点,返回插入后的根节点
class Solution {
public:TreeNode* insertIntoBST(TreeNode* root, int val) {if (root == nullptr) {// 找到空位置插入新节点return new TreeNode(val);}// 去右子树找插入位置if (root->val < val) {root->right = insertIntoBST(root->right, val);}// 去左子树找插入位置if (root->val > val) {root->left = insertIntoBST(root->left, val);}// 返回 root,上层递归会接收返回值作为子节点return root;}
};

4、在 BST 中删除一个数

这个问题稍微复杂,跟插入操作类似,先「找」再「改」,先把框架写出来再说:

TreeNode* deleteNode(TreeNode* root, int key) {if (root->val == key) {// 找到啦,进行删除} else if (root->val > key) {// 去左子树找root->left = deleteNode(root->left, key);} else if (root->val < key) {// 去右子树找root->right = deleteNode(root->right, key);}return root;
}

找到目标节点了,比方说是节点 A,如何删除这个节点,这是难点。因为删除节点的同时不能破坏 BST 的性质。有三种情况,用图片来说明。

  • 情况 1:A 恰好是末端节点,两个子节点都为空,那么它可以直接被删除。
    在这里插入图片描述

    if (root.left == null && root.right == null)return null;
    
  • 情况 2:A 只有一个非空子节点,那么它要让这个孩子接替自己的位置。
    在这里插入图片描述

    // 排除了情况 1 之后
    if (root.left == null) return root.right;
    if (root.right == null) return root.left;
    
  • A 有两个子节点,麻烦了,为了不破坏 BST 的性质,A 必须找到 左子树中最大的那个节点,或者右子树中最小的那个节点 来接替自己。我们以第二种方式讲解。
    在这里插入图片描述

    if (root.left != null && root.right != null) {// 找到右子树的最小节点TreeNode minNode = getMin(root.right);// 把 root 改成 minNoderoot.val = minNode.val;// 转而去删除 minNoderoot.right = deleteNode(root.right, minNode.val);
    }
    

三种情况分析完毕,填入框架,最终的代码:

class Solution {
public:// 定义:在以 root 为根的 BST 中删除值为 key 的节点,返回完成删除后的根节点TreeNode* deleteNode(TreeNode* root, int key) {if (root == nullptr) return nullptr;if (root->val == key) {// 这两个 if 把情况 1 和 2 都正确处理了if (root->left == nullptr) return root->right;if (root->right == nullptr) return root->left;// 处理情况 3// 获得右子树最小的节点TreeNode* minNode = getMin(root->right);// 删除右子树最小的节点root->right = deleteNode(root->right, minNode->val);// 用右子树最小的节点替换 root 节点minNode->left = root->left;minNode->right = root->right;root = minNode;} else if (root->val > key) {root->left = deleteNode(root->left, key);} else if (root->val < key) {root->right = deleteNode(root->right, key);}return root;}TreeNode* getMin(TreeNode* node) {// BST 最左边的就是最小的while (node->left != nullptr) node = node->left;return node;}
};

上述代码在处理情况 3 时通过一系列略微复杂的链表操作交换 root 和 minNode 两个节点:

// 获得右子树最小的节点
TreeNode* minNode = getMin(root->right);
// 删除右子树最小的节点
root->right = deleteNode(root->right, minNode->val);	//妙笔:用原函数递归删除
// 用右子树最小的节点替换 root 节点
minNode->left = root->left;
minNode->right = root->right;
root = minNode;

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

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

相关文章

GitHub Actions打包容器,推送 AWS ECR 并使 EKS 自动拉取以完成发版部署

以下是关于 EKS 直接拉取 ECR 镜像的解答&#xff0c;以及如何通过 GitHub Actions 将项目打包为容器、推送至 AWS ECR 并使 EKS 自动拉取以完成发版部署的详细步骤。当前时间为 2025 年 7 月 23 日下午 12:27 HKT&#xff0c;基于最新技术实践提供方案。1. EKS 直接拉取 ECR 镜…

洛谷刷题7.24

P1087 [NOIP 2004 普及组] FBI 树 - 洛谷 简单的二叉树遍历 #include<bits/stdc.h> #define ll long long using namespace std; int n; char show(string s){if(s.find(1)string::npos) return B;if(s.find(0)string::npos) return I;return F; } void dfs(string s){…

FreeRTOS—二值信号量

文章目录一、二值信号量简介二、二值信号量相关的API函数2.1.动态方式创建二值信号量2.2.获取信号量2.3.释放信号量三、实验3.1.实验设计3.2.软件设计一、二值信号量简介 二值信号量的本质是一个队列长度为 1 的队列&#xff0c;该队列就只有空和满两种情况&#xff0c;也就是…

挖掘录屏宝藏:Screenity 深度解析与使用指南

挖掘录屏宝藏&#xff1a;Screenity 深度解析与使用指南 在数字内容创作与信息分享日益频繁的今天&#xff0c;录屏软件成为了众多创作者、教育者和办公族的必备工具。今天&#xff0c;我要给大家介绍一款在 GitHub 上收获了大量关注的开源录屏软件 ——Screenity。它功能强大…

4.1.2 XmlInclude 在 C# 中的作用及示例

xmlInclude 是 .NET 中用于 XML 序列化的一个重要特性,XmlInclude 的主要作用是: 1.告知 XML 序列化器可能遇到的派生类型 2.解决多态类型的序列化和反序列化问题 3.允许基类序列化时包含派生类信息 当你有基类引用指向派生类对象时,如果不使用 XmlInclude,序列化器…

ARM汇编常见伪指令及其用法示例

伪指令不是指令&#xff0c;伪指令和指令的根本区别是经过编译后会不会生成机器码。 伪指令的意义在于指导编译过程。 伪指令是和具体的编译器相关的&#xff0c;我们使用gnu工具链&#xff0c;因此学习gnu环境下的汇编伪指令。在 ARM 汇编中&#xff0c;伪指令&#xff08;Pse…

算法调试技巧

引言算法调试常比编写更耗时&#xff0c;尤其是动态规划、递归等逻辑复杂的代码。本文分享一套系统化的调试方法&#xff0c;帮助快速定位问题。一、调试前的准备代码格式化使用统一缩进&#xff08;4 空格&#xff09;和命名规范&#xff0c;避免因格式混乱导致的逻辑误读。边…

每日功能分享|让观看者体验“无缝链接”观看的功能——视频自动续播功能

你是否遇到过这样的困扰——看到一半的视频&#xff0c;关闭后却忘记进度&#xff0c;再打开时需要手动拖拽寻找上次的观看位置&#xff1f;如今&#xff0c;“视频自动续播功能”完美解决了这一痛点&#xff01;无论是在线教育课程、影视剧集还是企业内部员工培训&#xff0c;…

AWS: 云上侦探手册,七步排查ALB与EC2连接疑云

今天&#xff0c;咱们来聊一个对于许多刚接触AWS的运维同学来说&#xff0c;既常见又有点头疼的话题&#xff1a;如何优雅地排查和解决AWS上ALB&#xff08;Application Load Balancer&#xff09;暴露EC2服务时遇到的种种疑难杂症。 最近&#xff0c;我刚帮一个朋友解决了类似…

EIDE 创建基于STM32-HD的项目快速创建流程

EIDE 创建基于STM32-HD的项目流程芯片系列定义宏Flash 大小RAM 大小STM32F10x_HD#define STM32F10X_HD256KB~512KB48KB~64KBSTM32F10x_MD#define STM32F10X_MD64KB~128KB20KBSTM32F10x_LD#define STM32F10X_LD16KB~32KB4KB~10KB 新建项目远程仓库获取裸机开发程序STM(意法半导体…

使用 QLExpress 构建灵活可扩展的业务规则引擎

目录 一、什么是 QLExpress&#xff1f; 二、推荐系统中的规则脚本应用 1 场景描述 2 推荐规则脚本&#xff08;QLExpress&#xff09; 3 系统实现 4 执行结果 5 推荐系统应用建议 三、风控系统中的规则判定 1 场景描述 2 风控规则脚本&#xff08;QLExpress&#xff…

【硬件-笔试面试题】硬件/电子工程师,笔试面试题-13,(知识点:DC-DC电源,相位裕度,增益裕度)

目录 1、题目 2、解答 相位裕度 增益裕度 3、相关知识点 一、波特图 二、相位裕度 三、增益裕度 四、在 DC - DC 电源中的应用 【硬件-笔试面试题】硬件/电子工程师&#xff0c;笔试面试题汇总版&#xff0c;持续更新学习&#xff0c;加油&#xff01;&#xff01;&a…

学生信息管理系统 - HTML实现增删改查

学生信息管理系统 - HTML实现增删改查 效果图 代码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…

Agile简介

Agile&#xff08;敏捷&#xff09;是一种软件开发方法论&#xff0c;核心是通过快速迭代、灵活响应变化&#xff0c;解决传统软件开发中周期长、需求变更困难等问题&#xff0c;最终高效交付符合用户实际需求的产品。 一、Agile 的起源&#xff1a;为什么需要敏捷&#xff1f;…

关于 URL 中 “+“ 号变成空格的问题

当你在 URL 中传递参数时&#xff0c;加号 () 会被自动转换为空格&#xff0c;这是 URL 编码的标准行为。问题原因在 URL 中&#xff1a;空格会被编码为 号当 URL 被解码时&#xff0c; 号又会被转换回空格这会导致原始数据中的 号丢失解决方案你需要对参数值进行正确的 URL …

综合实验(2)

文章目录 目录 文章目录 前言 OSPF运行在GRE隧道概述 典型应用场景 OSPF over GRE 配置 总结 前言 OSPF运行在GRE隧道概述 GRE&#xff08;Generic Routing Encapsulation&#xff09;隧道是一种通过封装原始数据包在IP网络中创建虚拟点对点连接的隧道技术。OSPF&#xff08;…

【应急响应工具教程】司稽(Whoamifuck):纯Shell打造的Linux应急响应利器

1、工具简介司稽&#xff08;Whoamifuck或Chief-Inspector,简称"who"&#xff09;&#xff0c;永恒之锋发布的第一款开源工具&#xff0c;这是一款由shell编写的Linux应急响应脚本&#xff0c;能对基本的检查项进行输出和分析&#xff0c;并支持一些扩展的特色功能。…

新手操作steam搬砖项目,应该如何快速起步

大家好哦&#xff0c;我是阿阳&#xff0c;今天继续给大家分享一些steam搬砖的知识。在我们操作过程中&#xff0c;问题问得最多的就是&#xff0c;新手应该怎么做&#xff1f;首先&#xff0c;那我们得先来了解-下,什么是steam搬砖,它的项目原理是什么&#xff0c;其次针对于这…

rt-thread加一个库

背景 官方软件包里没有的 可以以库或组件形式加入 本次仅为了验证&#xff0c;加到库 过程 下载源码 假设为 lib_demo 自己的板子目录为bsp/stm32 代码目录结构 bsp/stm32librarieslib_demo //新建文件夹src //把lib_demo里源码文件放进来inc //把lib_demo里头文件放进来SConsc…

c++深拷贝和浅拷贝

一、浅拷贝本质&#xff1a;简单地复制对象的成员值。如果成员里有指针&#xff0c;新对象和原对象的指针会指向同一块内存。比如你有对象 A&#xff0c;里面指针 p 指向堆内存 0x123&#xff1b;用 A 拷贝出对象 B&#xff0c;B 的指针 p 也指向 0x123。问题&#xff1a;若其中…