99.恢复搜索二叉树(morris遍历)

在这里插入图片描述

类似题型[114.二叉树展开为链表] :
 题目link
 解答link

 中序遍历的结果就是二叉搜索树所表示的有序数列,如中序遍历为 1 2 3 4 5 6 7 ,任意交换两节点由两种情况:
 1、交换相邻两数 : 1 2 4 3 5 6 7
 2、交换不相邻两数: 1 6 3 4 5 2 7

相邻两数只有一个逆序对, 交换之即可;
不相邻有两个逆序对,需要交换的是第一个逆序对的前一个数6与第二个逆序对的后一个数2。

如果是在空间复杂度为1的要求下, 使用morris算法如下:

Morris算法流程

 1、如果当前节点没有左儿子,则打印当前节点的值,然后进入右子树;
 2、如果 . 当前节点有左儿子,则寻找当前节点的前驱节点。
  (1) 如果前驱节点的右儿子为空,说明左子树没有遍历过,则进入左子树遍历,并且将前驱节点的右儿子置为当前节点,方便回溯;
  (2) 如果前驱节点的右儿子为当前节点,说明左子树已经被遍历过,则将前驱节点的右儿子恢复为空,然后打印该节点的值,然后进入右子树继续遍历。

下图为具体示例:
在这里插入图片描述

几个问题:
1、如何寻找一个节点的前驱节点?
 首先进入左孩子,然后一直寻找右孩子,直至没有右孩子的那个节点即为前驱节点。

2、如何原地寻找逆序对?
 需要一个父节点来记录。

代码

/**
 * 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* last = NULL, *first = NULL, *second = NULL;
        while (root) {
            if (!root->left) {
                if(last && last->val > root->val) {
                    if (!first) first = last;
                    second = root;
                }
                last = root;
                root = root->right;
            }
            else {
                auto p = root->left;
                while (p->right && p->right != root) p = p->right;  // 前驱节点p
                if (!p->right) {
                    p->right = root;
                    root = root->left;      //直接进入左节点,不需要进行比较
                }
                else {
                    p->right = NULL;
                    if(last && last->val > root->val) {
                    if (!first) first = last;
                    second = root;
                    }
                    last = root;
                    root = root->right;
                }
            }
        }
        swap(first->val, second->val);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值