【LeetCode】 99. Recover Binary Search Tree [Hard] [Morris Traversal] [Tree]
Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/recover-binary-search-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
【题意分析】
对于一个BST应该了解的一个重要特点就是,对其进行中序遍历可得到一个有小到大排序的数组。假设某个BST中序遍历后得到数组:[1,5,7,12,36,48];
当其中两个节点值互换后,会打破这个数组的局部有序性。例如上个例子中,如果交换7和36, 就可得到:[1,5,36,12,7,48].
由此可见,我们只要对这个树进行中序遍历,并在遍历过程中查看前一个节点值是否小于当前节点值。若不小于的话,则两节点中至少有一个节点是被互换过得。
【实现思路】
设置pre->前一个遍历过的节点, curr->当前在遍历的节点。
由上一个例子中可知:
第一次出现逆序情况时,pre是被互换过的节点;
最后一次出现逆序情况时,curr是被互换过的节点。
所以做题的步骤便是,先通过中序遍历和检查pre, curr指向的两节点值,找到被互换过的两节点。再把他们的值swap就可以了。
本题另一个难点是题目的follow up:
Follow up:
A solution using O(n) space is pretty straight forward.
Could you devise a constant space solution?
一般的树的遍历都需要 O(n)的空间复杂度,下面介绍一种算法“Morris Traversal"可以实现空间复杂度为O(1)的树的遍历。先来看一下使用这种算法遍历树的具体实现:
【Morris Traversal】(Inorder)
最常见得树的遍历方法就是使用递归或stack进行DFS。这种情况下需要花费额外的空间复杂度:O(n), n为节点个数。下边是用栈来实现的代码;递归代码很简单在此忽略。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
while(){
while(root) {
st.push(root);
root = root->left;
} //找到当前节点为根节点的子树中最左叶子节点,即为下一个要访问节点
if(st.empty()) return res;
root = st.top();
res.push_back(root->val);
st.pop();
root = root->right;
//下一个要访问节点,为当前被访问节点右子树中最左叶子节点;
//若右子树为空,则为父节点以及各祖先节点中第一个以该节点为左子树节点的祖先节点,即当前栈顶元素
}
return res;
}
};
由此可见,之所以需要栈,或者递归方程,是因为树的访问是自上而下的,没有从叶子节点指向父节点或某上方节点的指针。而在寻找下一个被访问节点时,如果不是该节点右子树的最左叶节点,则为上方第一个以该节点为左子树节点的祖先节点。
如果我们每次访问一个节点时,先找到他的相邻前置节点,并把这个前置节点的右子树指针指向该节点,类似于搭一个桥,那么就可以通过每一个节点的右子树指针找到下一个要访问的节点或节点群。要注意当沿着这个新加入的指针链接找到节点后,要删除这个链接以维持树的原本形态。
class Solutionpublic:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
while(root){
if(root->left){
TreeNode* curr = root->left;
while(curr->right != NULL && curr->right != root) curr = curr->right;
//每访问一个节点时,先找该几点相邻的先序遍历节点
if(curr->right == NULL) {
curr->right = root;
root = root->left;
//找到先序节点以后搭桥,然后寻找当前要访问的第一个节点
}
else { //访问当前节点部分A
curr->right = NULL;
res.push_back(root->val);
root = root->right;
//若先序遍历节点右子树节点已指向当前节点,意味着当前节点之前的所有节点已被遍历过一遍;
//所以当前节点即为要访问节点。将刚才搭的桥移除,并继续访问下一个节点或节点群
}
}
else { //访问当前节点部分B
res.push_back(root->val);
root = root->right;
//若当前节点没有左子树,意味着没有先序遍历的节点。
}
}
return res;
}
};
针对这道题目,只要在上方代码”访问当前节点部分A"和"访问当前节点B"两个地方做修改,并加入一个pre的节点指针就可以了。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void recoverTree(TreeNode* root) {
TreeNode* p1 = NULL;
TreeNode* p2 = NULL;
TreeNode* pre = new TreeNode(INT_MIN);
while(root){
if(root->left){
TreeNode* temp = root->left;
while(temp->right && temp->right != root) temp = temp->right;
if(!temp->right){
temp->right = root;
root = root->left;
}
else {
temp->right = NULL;
if(pre->val > root->val){
if(!p1) {
p1 = pre;
p2 = root;
}
else {
p2 = root;
}
}
pre = root;
root = root->right;
}
}
else {
if(pre->val > root->val){
if(!p1) {
p1 = pre;
p2 = root;
}
else {
p2 = root;
}
}
pre = root;
root = root->right;
}
}
int temp = p1->val;
p1->val = p2->val;
p2->val = temp;
}
};
【易错点】
不可以在未遍历完整个树之前就break, 会导致一些被搭的“桥”没有拆掉,所以改变了树的结构。
【LeetCode】 99. Recover Binary Search Tree [Hard] [Morris Traversal] [Tree]的更多相关文章
- 【LeetCode】99. Recover Binary Search Tree 解题报告(Python)
[LeetCode]99. Recover Binary Search Tree 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/p ...
- 【LeetCode】99. Recover Binary Search Tree
Recover Binary Search Tree Two elements of a binary search tree (BST) are swapped by mistake. Recove ...
- 【一天一道LeetCode】#99. Recover Binary Search Tree
一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Two ele ...
- 【LeetCode】95. Unique Binary Search Trees II 解题报告(Python)
[LeetCode]95. Unique Binary Search Trees II 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzh ...
- Leetcode 笔记 99 - Recover Binary Search Tree
题目链接:Recover Binary Search Tree | LeetCode OJ Two elements of a binary search tree (BST) are swapped ...
- LeetCode OJ 99. Recover Binary Search Tree
Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...
- 【LeetCode】1008. Construct Binary Search Tree from Preorder Traversal 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 日期 题目地址:https://leetcod ...
- 【LeetCode】98. Validate Binary Search Tree 解题报告(Python & C++ & Java)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 BST的中序遍历是有序的 日期 题目地址:ht ...
- 【LeetCode】98. Validate Binary Search Tree
题目: Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is define ...
随机推荐
- 动态规划-买卖股票的最佳时机 V
2020-03-11 18:19:00 问题描述: 给出一个股票n天的价格,每天最多只能进行一次交易,可以选择买入一支股票或卖出一支股票或放弃交易,输出能够达到的最大利润值 样例 样例 1: 给出 ` ...
- OpenCV-Python教程简介 | 一
OpenCV OpenCV由Gary Bradsky于1999年在英特尔创立,第一版于2000年问世.Vadim Pisarevsky加入Gary Bradsky,一起管理英特尔的俄罗斯软件OpenC ...
- 关于 JavaScript 的 精度丢失 与 近似舍入
一.背景 最近做 dashborad 图表时,涉及计算小数且四舍五入精确到 N 位.后发现 js 算出来的结果跟我预想的不一样,看来这里面并不简单-- 二.JS 与 精度 1.精度处理 首先明确两点: ...
- [vijos1574]摇钱树<dp+贪心>
题目链接:https://vijos.org/p/1574 这道题是昨晚一个叫Ztravellers的大佬推荐的,确实觉得这是一道很有意思的题,很多方面都很有意思: 初见这道题,估计想法都是贪心,因为 ...
- C#中的字符串处理
C#中的字符串处理 是由多个单个字符组成的.字符串的关键字是string,而我们单个字符char型.也就是一个字符串可以分为很多个char的字符.注意 同时,我们在开发项目或者学习时.更多的操作不是数 ...
- MQTT协议实现Android中的消息收发
前言 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输),基于发布/订阅范式的消息协议,是一种极其简单和轻量级的消息协议,专为受限设备和低带宽.高延迟 ...
- (C#、JavaScript)面向对象的程序设计
面向对象(OOP)的理解 喜欢程序的朋友们,大家应该都听过一句话"万物皆对象",感觉老牛X了. 面向对象的程序设计,它是围绕真实世界来设计程序的. 面向对象三要素:封装.继承.多态 ...
- Vue点击当前元素添加class 去掉兄弟的class
<div id="app"> <ul> <li v-for="(todo, index) in todos" v-on:click ...
- php 直接跳出嵌套循环
break 结束当前 for,foreach,while,do-while 或者 switch 结构的执行. break 可以接受一个可选的数字参数来决定跳出几重循环. <?php $arr = ...
- Vue 实战项目: 硅谷外卖(1)
第 1 章: 准备 1.1. 项目描述 1) 此项目为外卖 WebApp(SPA) 2) 包括商家, 商品, 购物车, 用户等多个子模块 3) 使用 Vue 全家桶+ES6+Webpack 等前端最新 ...