99. 恢复二叉搜索树

181 阅读3分钟

99. 恢复二叉搜索树

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

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

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/re… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 方法一:显式中序遍历

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    public:
        void inorder(TreeNode* root, vector<int>& nums) {
            if (root == nullptr) return;
            inorder(root->left, nums);
            nums.push_back(root->val);
            inorder(root->right, nums);
        }
        pair<int, int> findTwoSwapped(vector<int>& nums) {
            int x = -1, y = -1;
            for (int i = 0; i < nums.size() - 1; ++i)
            {
                if (nums[i + 1] < nums[i]) {
                    y = nums[i + 1];
                    if (x == -1) {
                        x = nums[i];
                    } else  {
                        break;
                    }
                }
            }
            // x为需交换的大值,y为需要交换的小值
            return {x, y};
        }
        void recover(TreeNode* r, int count, int x, int y) {
            if (r == nullptr) {
                return;
            }
            // 更新对应位置的值
            if (r->val == x || r->val == y) {
                r->val = r->val == x ? y : x;
                if (--count == 0) {
                    return;
                }
            }
            // 深度优先遍历
            recover(r->left, count, x, y);
            recover(r->right, count, x, y);
        }
        void recoverTree(TreeNode* root) {
            vector<int> nums;
            // 中序遍历存储增序数组
            inorder(root, nums);
            pair<int, int> swapped = findTwoSwapped(nums);
            recover(root, 2, swapped.first, swapped.second);
        }
};


方法二:隐式中序遍历


/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void recoverTree(TreeNode* root) {
        stack<TreeNode*> stack;
        TreeNode* pred = nullptr;
        TreeNode* x = nullptr;
        TreeNode* y = nullptr;
        // 根 左节点和左子孙节点存入栈
        // 对每一个节点比较对应的父右节点
        while(root != nullptr || !stack.empty()) {
            while(root != nullptr) {
                stack.push(root);
                root = root->left;
            }
            root = stack.top();
            stack.pop();    
            // x代表错序大值,y代表后面小值
            if (pred != nullptr && root->val < pred->val) {
                y = root;
                if (x == nullptr) {
                    x = pred;
                } else {
                    break;
                }
            }
            pred = root;
            root = root->right;
        }
        swap(x->val, y->val);
    }
};

方法三:Morris 中序遍历

morris遍历的实现原则:

  • 记作当前节点为cur。
  • 如果cur无左孩子,cur向右移动(cur=cur.right)
  • 如果cur有左孩子,找到cur左子树上最右的节点,记为mostright
  • 如果mostright的right指针指向空,让其指向cur,cur向左移动(cur=cur.left)
  • 如果mostright的right指针指向cur,让其指向空,cur向右移动(cur=cur.right)
class Solution {
public:
    void recoverTree(TreeNode* root) {
        TreeNode *x = nullptr, *y = nullptr, *pred = nullptr, *predecessor = nullptr;

        while (root != nullptr) {
            if (root->left != nullptr) {
                // predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
                predecessor = root->left;
                while (predecessor->right != nullptr && predecessor->right != root) {
                    predecessor = predecessor->right;
                }
                
                // 让 predecessor 的右指针指向 root,继续遍历左子树
                if (predecessor->right == nullptr) {
                    predecessor->right = root;
                    root = root->left;
                }
                // 说明左子树已经访问完了,我们需要断开链接
                else {
                    if (pred != nullptr && root->val < pred->val) {
                        y = root;
                        if (x == nullptr) {
                            x = pred;
                        }
                    }
                    pred = root;

                    predecessor->right = nullptr;
                    root = root->right;
                }
            }
            // 如果没有左孩子,则直接访问右孩子
            else {
                if (pred != nullptr && root->val < pred->val) {
                    y = root;
                    if (x == nullptr) {
                        x = pred;
                    }
                }
                pred = root;
                root = root->right;
            }
        }
        swap(x->val, y->val);
    }
};

二次理解Morris

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void recoverTree(TreeNode* root) {
        TreeNode *x = nullptr, *y = nullptr, *pred = nullptr, *prev = nullptr;
        while(root != nullptr) {
            if (root->left != nullptr) {
                // 开始左子树
                prev = root->left;
                // 遍历左子树的右子节点,找到最右
                while(prev->right != nullptr && prev->right != root) {
                    prev = prev->right;
                }
                // 最右不是空,断开,左子树访问完;最右是空 继续访问左子树
                if (prev->right != nullptr) {
                    if (pred != nullptr && pred->val > root->val) {
                        y = root;
                        if (x == nullptr) {
                            x = pred;
                        }
                    }
                    // 左子树访问完 断开链接
                    prev->right = nullptr;
                    // 更新pred和root
                    pred = root;
                    root = root->right;
                } else {
                    // 左子树未访问完,继续,加上链接指向root
                    prev->right = root;
                    root = root->left;
                }
            } else {
                if (pred != nullptr && pred->val > root->val) {
                    y = root;
                    if (x == nullptr) {
                        x = pred;
                    }
                }
                pred = root;
                root = root->right;
            }
        }
        swap(x->val, y->val);
    }
};