美文网首页
99. 恢复二叉搜索树

99. 恢复二叉搜索树

作者: 水中的蓝天 | 来源:发表于2022-07-28 00:02 被阅读0次

    99. 恢复二叉搜索树

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

    思路:

    1. 利用二叉搜索树 中序遍历结果是升序的特点找到错误逆序对
    2. 交换两个错误节点
    

    复杂度分析:

    时间复杂度: O(n) 因为只有一个中序遍历
    空间复杂度: O(n) , 二叉树的空间复杂度就是树的高度, 普通情况是O(H),但是最坏情况所有结点都在一侧时高度就等于结点数 H = N

    寻找错误逆序对核心逻辑.png
    /**
    思路:
    1.利用二叉搜索树中序遍历的结果是升序的特点找出错误逆序对
    2.交换两个错误结点
    时间复杂度:O(n)
    空间复杂度:O(n)
    */
    class Solution1 {
      
      TreeNode prev;//前置结点
      TreeNode first;//第一个错误结点
      TreeNode second;//第二个错误结点
      public void recoverTree(TreeNode root) {
    
         //1.寻找错误结点
         findWeongNodes(root);
    
         //2.恢复错误结点
         int tmp = first.val;
         first.val = second.val;
         second.val = tmp;
    
      }
      
      //递归 中序遍历寻找错误结点
      private void findWeongNodes(TreeNode root){
        
        //0.递归退出条件
        if(root==null) return;
        
        //1.搜索左边
        findWeongNodes(root.left);
    
        //2.当前结点是否是错误结点
        if(prev != null && prev.val > root.val) {//前置结点不为空 且 前置结点的值 大于 当前结点
            //第二个错误结点:最后一个逆序对中较小的结点
            second = root;
            if(first!=null) return;//first不为空说明已经找到了第一个错误结点(第一个逆序对中较大的结点)
            //来到这里说明还没找到第一个错误结点,那么prev就是这个结点
            first = prev;
        }
    
        prev = root;//重点:保留当前结点,成为下一次遍历的前置结点
    
        //3.搜索右边
        findWeongNodes(root.right);
    
      }
    
    }
    
    
    
    执行结果.png

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

    思路:使用二叉树的Morris遍历,也叫线索二叉树 可以实现时间复杂度O(n) 空间复杂度O(1)

    执行步骤.png
    
    /**
     时间复杂度: O(n) 实际上是O(2n) 因为一个结点会走两次
     空间复杂度: O(1)
     */
    class Solution {
    
        public TreeNode prev;
        public TreeNode first;
        public TreeNode second;
        
        //根据中序遍历升序的规律找到错误节点
        private void findNode(TreeNode root) {
            
            //结束条件
            if(root==null) return;
            //前一个节点不为空 且val的值大于当前节点的val值
            if(prev != null && prev.val > root.val) {
                //第二个错误节点: 最后一个逆序对中较小的那个节点
                second = root;
                //first不为空说明已经获得了第一个错误节点:第一个逆序对中较大的那个节点
                if(first != null) return;
                first = prev;
            }
    
            //保留当前节点的成为下次遍历的前一个节点
            prev = root;
    
        }
    
    
        public void recoverTree(TreeNode root) {
    
            //1. Morris遍历
            TreeNode node = root;
            while(node != null) {
                //1.1 左子树不为空
                 if(node.left != null) {
    
                    //找到前驱节点条件 向右走没有新的节点了 且 遇到的不是自己
                    TreeNode pred = node.left;
                    while(pred.right != null && pred.right != node) {
                        pred = pred.right;
                    }
    
                    //前驱节点的右子树为空,修改其right指向自己 
                    if(pred.right == null) {
                        pred.right = node;
                        node = node.left;
                    }else {//前驱节点的右子树不为空,就清空 因为此时已经走回来了 此时pred.right = node
                       findNode(node);
                       pred.right = null;
                       node = node.right;//向右搜索
                    }
                  
                 }else {//1.2 没有左子树了 说明到最左边了 然后往回走
                     findNode(node);
                     node = node.right;
                 }
            }
    
             //2. 交换两个错误节点
             int tmp = first.val;
             first.val = second.val;
             second.val = tmp;
    
        }
    
    }
    
    
    执行结果.png

    相关文章

      网友评论

          本文标题:99. 恢复二叉搜索树

          本文链接:https://www.haomeiwen.com/subject/ffetirtx.html