【9.3】树结构-恢复二叉搜索树

embedded/2025/1/15 6:08:37/

一、题目

        给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 

示例 1:

输入:root = [1,3,null,null,2]
输出:[3,1,null,null,2]
解释:3 不能是 1 的左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。

示例 2:

输入:root = [3,1,4,null,null,2]
输出:[2,1,4,null,null,3]
解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。

提示:

  • 树上节点的数目在范围 [2, 1000]
  • -231 <= Node.val <= 231 - 1

进阶:使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用 O(1) 空间的解决方案吗?

二、解题思路

2.1 递归方法

        这道题目涉及二叉搜索树中两个节点被错误交换的情况。要解决这个问题,首先需要理解二叉搜索树的性质。二叉搜索树的定义是:如果左子树不为空,则左子树上所有节点的值都小于根节点的值;如果右子树不为空,则右子树上所有节点的值都大于根节点的值;同时,左子树和右子树本身也必须是二叉搜索树。

        二叉搜索树的一个重要特性是,其中序遍历的结果是一个有序的序列。基于这一点,我们可以通过中序遍历来检测问题。具体来说,如果在遍历过程中发现当前节点的值小于前一个节点的值,就说明出现了逆序,这意味着至少有一个节点被错误地放置了。我们只需要找到这两个错误的节点,然后将它们的值交换回来即可解决问题。

        通过这种方法,我们可以有效地定位并修复二叉搜索树中的错误节点。

#include <iostream>
using namespace std;// 定义二叉树节点
struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};class Solution {
private:TreeNode* pre = nullptr;  // 当前节点的前一个节点TreeNode* first = nullptr;  // 第一个错误的节点TreeNode* second = nullptr;  // 第二个错误的节点public:void recoverTree(TreeNode* root) {// 递归中序遍历inorder(root);// 交换两个错误节点的值if (first && second) {swap(first->val, second->val);}}private:void inorder(TreeNode* root) {if (!root) return;// 遍历左子树inorder(root->left);// 检查是否出现逆序if (pre && pre->val > root->val) {if (!first) {first = pre;  // 第一个错误节点是 pre}second = root;  // 第二个错误节点是当前节点}pre = root;  // 更新 pre// 遍历右子树inorder(root->right);}
};// 打印二叉树的中序遍历结果(用于验证)
void printInorder(TreeNode* root) {if (!root) return;printInorder(root->left);cout << root->val << " ";printInorder(root->right);
}int main() {// 构建一个错误的二叉搜索树TreeNode* root = new TreeNode(3);root->left = new TreeNode(1);root->right = new TreeNode(4);root->right->left = new TreeNode(2);cout << "原始树的中序遍历: ";printInorder(root);cout << endl;// 修复二叉搜索树Solution solution;solution.recoverTree(root);cout << "修复后的中序遍历: ";printInorder(root);cout << endl;return 0;
}

2.2 非递归方法

        这道题的关键在于理解二叉搜索树的一个核心特性:**中序遍历的结果是一个有序的序列**。基于这一点,我们可以通过中序遍历来检测并修复树中两个节点被错误交换的问题。

        中序遍历的实现方式不仅限于递归,还可以通过非递归(迭代)的方式来完成。下面,我们将使用非递归的代码来实现这一逻辑,通过栈来模拟递归的过程,从而找到并修复错误的节点。

#include <iostream>
#include <stack>
using namespace std;// 定义二叉树节点
struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};class Solution {
public:void recoverTree(TreeNode* root) {stack<TreeNode*> stack;  // 存储节点的栈TreeNode* pre = nullptr;  // 当前节点的前一个节点TreeNode* first = nullptr;  // 第一个错误的节点TreeNode* second = nullptr;  // 第二个错误的节点while (root || !stack.empty()) {// 遍历左子树while (root) {stack.push(root);root = root->left;}// 访问当前节点if (!stack.empty()) {root = stack.top();stack.pop();// 检查是否出现逆序if (first == nullptr && pre != nullptr && pre->val > root->val) {first = pre;  // 第一个错误节点是 pre}if (first != nullptr && pre->val > root->val) {second = root;  // 第二个错误节点是当前节点}pre = root;  // 更新 pre// 遍历右子树root = root->right;}}// 交换两个错误节点的值if (first && second) {swap(first->val, second->val);}}
};// 打印二叉树的中序遍历结果(用于验证)
void printInorder(TreeNode* root) {if (!root) return;printInorder(root->left);cout << root->val << " ";printInorder(root->right);
}int main() {// 构建一个错误的二叉搜索树TreeNode* root = new TreeNode(3);root->left = new TreeNode(1);root->right = new TreeNode(4);root->right->left = new TreeNode(2);cout << "原始树的中序遍历: ";printInorder(root);cout << endl;// 修复二叉搜索树Solution solution;solution.recoverTree(root);cout << "修复后的中序遍历: ";printInorder(root);cout << endl;return 0;
}


http://www.ppmy.cn/embedded/154035.html

相关文章

Linux标准IOday5

1:思维导图 2:有一个隧道&#xff0c;长1000m&#xff0c;有一辆高铁&#xff0c;每秒100米&#xff0c;有一辆快车&#xff0c;每秒50m&#xff0c;要求模拟这两列火车通过隧道的场景 #include <stdio.h>#include <string.h>#include <unistd.h>#include &l…

Kotlin 快速上手指南:从安装 IntelliJ IDEA 到编写第一个程序

文章目录 什么是kotlinIntelliJ IDEA安装 IntelliJ IDEA创建 Kotlin 项目运行 Kotlin 程序更改进入后默认打开上一次项目的设置打开 IntelliJ IDEA进入设置:重新启动 IntelliJ IDEA:快速学习Kotlin变量声明类型推断条件表达式定义函数单表达式函数when 表达式when 语句的基本…

leetcode131.分割回文串

给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 示例 1&#xff1a; 输入&#xff1a;s "aab" 输出&#xff1a;[["a","a","b"],["aa","b&…

MySQL 16 章——变量、流程控制和游标

一、变量 在MySQL数据库的存储过程和存储函数中&#xff0c;可以使用变量来存储查询或计算的中间结果数据&#xff0c;或者输出最终的结果数据 在MySQL数据库中&#xff0c;变量分为系统变量和用户自定义变量 &#xff08;1&#xff09;系统变量 1.1.1系统变量分类 变量由…

PPT素材免费下载

下载免费的PPT模板、素材、背景、图表、课件等等就上这6个网站&#xff0c;赶紧收藏&#xff01; 1、菜鸟图库 ppt模板免费下载|ppt背景图片 - 菜鸟图库 菜鸟图库网有非常丰富的免费素材&#xff0c;像设计类、办公类、自媒体类等素材都很丰富。PPT模板种类很多&#xff0c;全…

06_Redis数据类型-List列表

1.List列表介绍 在Redis的List数据类型中,元素以字符串形式存在,并按照它们被插入的顺序进行有序排列。List允许元素重复,即相同元素可以被多次添加到列表中。每个List的容量上限为2的32次方减1,也就是4294967295个元素。我们可以添加一个新元素到List列表的头部(左边)或…

[微服务]redis数据结构

介绍 我们常用的Redis数据类型有5种&#xff0c;分别是&#xff1a; StringListSetSortedSetHash 还有一些高级数据类型&#xff0c;比如Bitmap、HyperLogLog、GEO等&#xff0c;其底层都是基于上述5种基本数据类型。因此在Redis的源码中&#xff0c;其实只有5种数据类型。 …

深度学习与通信技术的融合:未来的创新与机遇

目录 引言&#xff1a;深度学习与通信技术的结合深度学习在通信领域的应用深度学习与通信技术融合的前景与挑战博雅智信的辅导模式学术诚信声明 引言&#xff1a;深度学习与通信技术的结合 随着信息技术的飞速发展&#xff0c;深度学习在多个领域取得了显著进展。通信技术作为…