每日一题算法: 2020年8月8日[恢复二叉搜索树] recoverTree

2020年8月8日恢复二叉搜索树 recoverTree

在这里插入图片描述

/**
 * 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) {
        
    }
}

解题思路:

首先,给我们的一个二叉树是属于,搜索二叉树的定义就是在于每一个节点的左边子树上的值都要比根节点的值要大,右子树的每一个节点的值都比根节点的大。

那么我们应该能够得到一个节点的最小值节点和最大值节点。

一个节点的值必定小于在他右边的第一个父节点,必定小于在他左边的第一个父节点。

我们可以使用递归来判断一个节点的这两个父节点的值和当前节点的值的关系。设计这样的一个递归算法。我们输入三个节点,分别为最小值节点,最大值节点,节点本身,然后比较三个节点直接的值是否符合搜索树的规则,如果不符合,则交换那两个符合规则的节点。(比如本节点小于最小值节点,则交换两个节点的值)

疑问?有没有可能存在多个匹配异常但是只需要改动一处就能让整个二叉树匹配的方式呢?

比如下图:
在这里插入图片描述

20和19明显是不符合规则的,但是19和10也是不符合规则的,这种情况下如何判断该如何移动两个节点的值呢?

进过思考发现,这种情况最多出现两次,也就是说不会出现A->B不符合规则,B->C不符合规则,同时C->D不符合规则的情况。

证明如下,都用右子树来举例吧。

右子树的规则时右子树必须必根节点大,那么

在这里插入图片描述

A->B不符合规则,则A>B ,根节点大于右子树节点

B->C不符合规则,则B>C

此时交换AC的值就可以实现使二叉树符合规则

如果此时存在C->D不符合规则,无论如何交换两个值,都会存在不符合的节点。

我们已知A>B>C>D,如何交换两个节点的值使得结果变成A<B<C<D?
答案是不可能的。

所以,经过以上的分析,我们寻找整个二叉树中存在的两个(或者一个)位置异常,交换他们的值(一个位置时直接交换两个节点),两个位置时经过计算找到这两个节点进行交换。

算法改进:

在码代码的过程中发现,其实只需要找到一个错误,记录下这个错误的值,然后找到他应该存在的位置,找到这个值的位置必定就是另一个错误的值。

比如下图:

一开始就找出了存在错误[20,19]

判断19是否应该在该位置,那么假设19是错误的那个查找19应该存在的位置。

结果是找不到,应该19如果放在6的位置上,6任然不能匹配19的位置,那么错误的肯定是20。

那么在19下面查找20的位置,找到了10的位置,把10和20的位置交换。
在这里插入图片描述

代码部分:

没做出来。逻辑有些复杂,写了两个小时也没写出来。。。

如果是题目中给出的使用空间复杂度复杂度为n的话没有问题,直接将原二叉树遍历一遍加入新的二叉树中就可以了。

官方解答:

思路是差不多的

class Solution {
    public void recoverTree(TreeNode root) {
        List<Integer> nums = new ArrayList<Integer>();
        inorder(root, nums);
        int[] swapped = findTwoSwapped(nums);
        recover(root, 2, swapped[0], swapped[1]);
    }

    public void inorder(TreeNode root, List<Integer> nums) {
        if (root == null) {
            return;
        }
        inorder(root.left, nums);
        nums.add(root.val);
        inorder(root.right, nums);
    }

    public int[] findTwoSwapped(List<Integer> nums) {
        int n = nums.size();
        int x = -1, y = -1;
        for (int i = 0; i < n - 1; ++i) {
            if (nums.get(i + 1) < nums.get(i)) {
                y = nums.get(i + 1);
                if (x == -1) {
                    x = nums.get(i);
                } else {
                    break;
                }
            }
        }
        return new int[]{x, y};
    }

    public void recover(TreeNode root, int count, int x, int y) {
        if (root != null) {
            if (root.val == x || root.val == y) {
                root.val = root.val == x ? y : x;
                if (--count == 0) {
                    return;
                }
            }
            recover(root.right, count, x, y);
            recover(root.left, count, x, y);
        }
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/recover-binary-search-tree/solution/hui-fu-er-cha-sou-suo-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值