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为节点个数。下边是用栈来实现的代码;递归代码很简单在此忽略。

  1. class Solution {
  2. public:
  3. vector<int> inorderTraversal(TreeNode* root) {
  4. vector<int> res;
  5. stack<TreeNode*> st;
  6. while(){
  7. while(root) {
  8. st.push(root);
  9. root = root->left;
  10. } //找到当前节点为根节点的子树中最左叶子节点,即为下一个要访问节点
  11. if(st.empty()) return res;
  12. root = st.top();
  13. res.push_back(root->val);
  14. st.pop();
  15. root = root->right;
  16. //下一个要访问节点,为当前被访问节点右子树中最左叶子节点;
  17. //若右子树为空,则为父节点以及各祖先节点中第一个以该节点为左子树节点的祖先节点,即当前栈顶元素
  18. }
  19. return res;
  20. }
  21. };

由此可见,之所以需要栈,或者递归方程,是因为树的访问是自上而下的,没有从叶子节点指向父节点或某上方节点的指针。而在寻找下一个被访问节点时,如果不是该节点右子树的最左叶节点,则为上方第一个以该节点为左子树节点的祖先节点。

如果我们每次访问一个节点时,先找到他的相邻前置节点,并把这个前置节点的右子树指针指向该节点,类似于搭一个桥,那么就可以通过每一个节点的右子树指针找到下一个要访问的节点或节点群。要注意当沿着这个新加入的指针链接找到节点后,要删除这个链接以维持树的原本形态。

  1. class Solutionpublic:
    vector<int> inorderTraversal(TreeNode* root) {
  2. vector<int> res;
  3. while(root){
  4. if(root->left){
  5. TreeNode* curr = root->left;
  6. while(curr->right != NULL && curr->right != root) curr = curr->right;
              //每访问一个节点时,先找该几点相邻的先序遍历节点
        
              if(curr->right == NULL) {
  7. curr->right = root;
  8. root = root->left;
                //找到先序节点以后搭桥,然后寻找当前要访问的第一个节点
  9. }
  10. else { //访问当前节点部分A
  11. curr->right = NULL;
  12. res.push_back(root->val);
  13. root = root->right;
                //若先序遍历节点右子树节点已指向当前节点,意味着当前节点之前的所有节点已被遍历过一遍;
                //所以当前节点即为要访问节点。将刚才搭的桥移除,并继续访问下一个节点或节点群
  14. }
  15. }
  16. else { //访问当前节点部分B
  17. res.push_back(root->val);
  18. root = root->right;
              //若当前节点没有左子树,意味着没有先序遍历的节点。
  19. }
  20. }
  21. return res;
  22. }
  23. };

针对这道题目,只要在上方代码”访问当前节点部分A"和"访问当前节点B"两个地方做修改,并加入一个pre的节点指针就可以了。

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  8. * };
  9. */
  10. class Solution {
  11. public:
  12. void recoverTree(TreeNode* root) {
  13. TreeNode* p1 = NULL;
  14. TreeNode* p2 = NULL;
  15. TreeNode* pre = new TreeNode(INT_MIN);
  16. while(root){
  17. if(root->left){
  18. TreeNode* temp = root->left;
  19. while(temp->right && temp->right != root) temp = temp->right;
  20. if(!temp->right){
  21. temp->right = root;
  22. root = root->left;
  23. }
  24. else {
  25. temp->right = NULL;
  26. if(pre->val > root->val){
  27. if(!p1) {
  28. p1 = pre;
  29. p2 = root;
  30. }
  31. else {
  32. p2 = root;
  33. }
  34. }
  35. pre = root;
  36. root = root->right;
  37. }
  38. }
  39. else {
  40. if(pre->val > root->val){
  41. if(!p1) {
  42. p1 = pre;
  43. p2 = root;
  44. }
  45. else {
  46. p2 = root;
  47. }
  48. }
  49. pre = root;
  50. root = root->right;
  51. }
  52. }
  53. int temp = p1->val;
  54. p1->val = p2->val;
  55. p2->val = temp;
  56. }
  57. };

【易错点】

不可以在未遍历完整个树之前就break, 会导致一些被搭的“桥”没有拆掉,所以改变了树的结构。

【LeetCode】 99. Recover Binary Search Tree [Hard] [Morris Traversal] [Tree]的更多相关文章

  1. 【LeetCode】99. Recover Binary Search Tree 解题报告(Python)

    [LeetCode]99. Recover Binary Search Tree 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/p ...

  2. 【LeetCode】99. Recover Binary Search Tree

    Recover Binary Search Tree Two elements of a binary search tree (BST) are swapped by mistake. Recove ...

  3. 【一天一道LeetCode】#99. Recover Binary Search Tree

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Two ele ...

  4. 【LeetCode】95. Unique Binary Search Trees II 解题报告(Python)

    [LeetCode]95. Unique Binary Search Trees II 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzh ...

  5. Leetcode 笔记 99 - Recover Binary Search Tree

    题目链接:Recover Binary Search Tree | LeetCode OJ Two elements of a binary search tree (BST) are swapped ...

  6. LeetCode OJ 99. Recover Binary Search Tree

    Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...

  7. 【LeetCode】1008. Construct Binary Search Tree from Preorder Traversal 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 日期 题目地址:https://leetcod ...

  8. 【LeetCode】98. Validate Binary Search Tree 解题报告(Python & C++ & Java)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 BST的中序遍历是有序的 日期 题目地址:ht ...

  9. 【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 ...

随机推荐

  1. Java并发包下锁学习第二篇Java并发基础框架-队列同步器介绍

    Java并发包下锁学习第二篇队列同步器 还记得在第一篇文章中,讲到的locks包下的类结果图吗?如下图: ​ 从图中,我们可以看到AbstractQueuedSynchronizer这个类很重要(在本 ...

  2. sql-lib闯关21-30

    第二十一关 base64编码,单引号,报错型,cookie型注入. 本关和less-20相似,只是cookie的uname值经过base64编码了,下图为我们输入万能密码显示 uname = YWRt ...

  3. 多伦多大学&NVIDIA最新成果:图像标注速度提升10倍!

    图像标注速度提升10倍! 这是多伦多大学与英伟达联合公布的一项最新研究:Curve-GCN的应用结果. Curve-GCN是一种高效交互式图像标注方法,其性能优于Polygon-RNN++.在自动模式 ...

  4. coding++:高并发解决方案限流技术--计数器--demo

    1.它是限流算法中最简单最容易的一种算法 计数器实现限流 每分钟只允许10个请求 第一个请求进去的时间为startTime,在startTime + 60s内只允许10个请求 当60s内超过十个请求后 ...

  5. extend()和append()的区别

    append()方法用于在列表末尾添加新的对象(对象可以是值或列表),一般用于添加列表项. extend()方法用于在列表末尾追加另一个序列中的多个值.

  6. Python3安装Crypto加密包

    Python3安装Crypto加密包 下载链接 加密包地址 步骤 下载加密包,解压加密包到Python安装目录下Lib\site-packages目录中,尝试在Pycharm中导入 from Cryp ...

  7. list容器排除重复单词的程序

    #include<iostream> #include<fstream> #include<string> #include<algorithm> #i ...

  8. DOM--选取文档元素

    大多数的客户端JavaScript程序在运行时都是在操作一个或者多个文档元素,而为了操作文档中的元素我们就必须要通过某种途径或者方法获得或者选取这些引用文档元素的Element对象.DOM定义了许多种 ...

  9. [转载]Docker容器无法被stop or kill问题

    来源: 问题过程 某环境一个mysql容器无法被stop or kill or rm sudo docker ps | grep mysql 查看该容器 7844250860f8 mysql:5.7. ...

  10. 《Three.js 入门指南》3.1.1 - 基本几何形状 - 球体(SphereGeometry)

    3.1 基本几何形状 球体(SphereGeometry) 构造函数: THREE.SphereGeometry(radius, segmentsWidth, segmentsHeight, phiS ...