【LeetCode-99】99.恢复二叉搜索树

恢复二叉搜索树

题目描述

二叉搜索树中的两个节点被错误地交换。

请在不改变其结构的情况下,恢复这棵树。
【LeetCode-99】99.恢复二叉搜索树_第1张图片
【LeetCode-99】99.恢复二叉搜索树_第2张图片

方法一:中序遍历O(n)

中序遍历过程中,记录错误两个错误排序节点,最后进行交换

递归遍历的空间复杂度是O(h),h为树度高度,本质上还是O(n)的

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    TreeNode pre, t1, t2;
    
    public void recoverTree(TreeNode root) {
        if(root == null) {
            return ;
        }
        //中序遍历是有序的(左<根<右),保存中序遍历当前节点的前一个结点
        //递归找不符合BST中序遍历规则的节点,只需要将值交换回去
        inorder(root);
        
        int temp = t1.val;
        t1.val = t2.val;
        t2.val = temp;
    }
    
    public void inorder(TreeNode root) {
        //1.终止条件
        if(root == null) {
            return ;
        }
        
        //2.访问左子树
        inorder(root.left);
        
        //3.访问根节点----对当前节点进行一些处理(在遍历过程中希望实现的操作)
        //因为二叉搜索树中的两个节点被错误地交换。中序遍历就变成了(左>根>右),找到要交换的两个节点
        if(pre != null && root.val < pre.val) {
            if(t1 == null) {
                t1 = pre;
            }
            t2 = root;
        }
        pre = root;
        
        //4.访问右子树
        inorder(root.right);
    }
}

方法二:

Morris序列可以做到O(1)的空间复杂度,二叉树里面的空节点没用被利用到,morris序列就是利用了null节点记录了返回的路径,所以不需要额外的空间辅助记录返回的路径

Morris遍历算法的步骤如下:

  • 检查当前结点的左孩子:

    • 如果当前结点的左孩子为空,说明要不没有前驱,要不前驱是它的父结点,所以进行检查,然后进入右孩子。
    • 如果当前结点的左孩子不为空,说明左子树里肯定有它的前驱,那就找到这个前驱
      • 如果前驱结点的右孩子是空,说明还没检查过当前节点的左子树,那么把前驱结点的右孩子指向当前结点,然后进入当前结点的左孩子。
      • 如果当前结点的前驱结点其右孩子指向了它本身,说明当前节点的左子树已被检查过,就直接进行检查,然后把前驱结点的右孩子设置为空,恢复原树,再进入右孩子。

Morris代码框架:

while(cur){
    if(cur->left == NULL){// 左子树为空时,直接比较,然后进入右子树
        /*************
        /*  进行你的操作
        *************/
        pre = cur;
        cur = cur->right;
    }else{// 进入左子树
        /*************
        /* 找cur的前驱结点,找到后分两种情况
        /*   1、cur的左子结点没有右子结点,那cur的左子结点就是前驱
        /*   2、cur的左子结点有右子结点,就一路向右下,走到底就是cur的前驱
        *************/
        TreeNode* preceesor = cur->left;
        while(preceesor->right && preceesor->right != cur){
            preceesor = preceesor->right;
        }

        // 前驱已经指向自己了,直接比较,然后进入右子树
        if(preceesor->right == cur){
            /*************
            /*  进行你的操作
            *************/
            preceesor->right = NULL; // 断开连接,恢复原树
            pre = cur;
            cur = cur->right;
        }else{ // 前驱还没有指向自己,说明左边还没有遍历,将前驱的右指针指向自己,后进入前驱
            preceesor->right = cur;
            cur = cur->left;
        }
    }
}

复杂度分析

  • 时间复杂度:每个结点访问了2次,因此时间复杂度为O(n)
  • 空间复杂度:只使用了常数空间,因此空间复杂度为O(1)

本题代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    public  void recoverTree(TreeNode root) {
        if (null == root) {
            return;
        }
        TreeNode node1 = null;
        TreeNode node2 = null;

        TreeNode mostRight = null;
        TreeNode cur = root;

        TreeNode pre = null;
        while (cur != null) {
            mostRight = cur.left;
            if (mostRight != null) {
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }
                if (mostRight.right == null) {
                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                } else {  // 第二次遍历到了
                    mostRight.right = null;
                }
            }

            if (pre != null && pre.val > cur.val) {
                node1 = node1 == null ? pre : node1;
                node2 = cur;
            }

            pre = cur;
            cur = cur.right;
        }

        int temp = node1.val;
        node1.val = node2.val;
        node2.val = temp;
    }

}

参考:
https://leetcode-cn.com/problems/recover-binary-search-tree/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-21/

你可能感兴趣的:(LeetCode,恢复二叉搜索树,Morris思想,中序遍历)