LeetCode099——恢复二叉搜索树

本文探讨了LeetCode上的二叉搜索树恢复问题,提供了三种解决方案,包括中序遍历比较、改进后的中序遍历以及进一步优化的中序遍历策略,详细分析了每种方法的时间和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我的LeetCode代码仓:https://github.com/617076674/LeetCode

原题链接:https://leetcode-cn.com/problems/recover-binary-search-tree/description/

题目描述:

知识点:二叉搜索树、中序遍历

思路一:中序遍历

这个思路和LeetCode098——验证二叉搜索树中的思路二是一致的。

对于一棵二叉搜索树而言,其中序遍历的结果是一个递增序列。我们保存原二叉搜索树中序遍历的结果。再对该结果进行排序后得到另一个序列,比较两个序列中的不同的两个值,即为需要交换的两个错误节点。

这里我采用的是递归的形式实现中序遍历,更多中序遍历算法请见LeetCode094——二叉树的中序遍历

时间复杂度是O(nlogn),其中n为树中的节点个数。空间复杂度也是O(n)。

JAVA代码:

public class Solution {

    List<TreeNode> list;

    public void recoverTree(TreeNode root) {
        list = new ArrayList<>();
        inorderTraversal(root);
        List<TreeNode> tempList = new ArrayList<>(list);
        Collections.sort(tempList, new Comparator<TreeNode>() {
            @Override
            public int compare(TreeNode treeNode1, TreeNode treeNode2) {
                return treeNode1.val - treeNode2.val;
            }
        });
        List<Integer> wrongList = new ArrayList<>();
        for(int i = 0; i < list.size(); i++){
            if(list.get(i).val != tempList.get(i).val){
                wrongList.add(i);
            }
        }
        change(list, wrongList.get(0), wrongList.get(1));
    }

    private void inorderTraversal(TreeNode root){
        if(root == null){
            return;
        }
        inorderTraversal(root.left);
        list.add(root);
        inorderTraversal(root.right);
    }

    private void change(List<TreeNode> list, int i, int j){
        Integer temp = list.get(i).val;
        list.get(i).val = list.get(j).val;
        list.get(j).val = temp;
    }
}

LeetCode解题报告:

思路二:思路一的改进

在思路一中,我们是通过对中序遍历所得结果进行排序,来比较两个序列的不同值来找出需要交换的两个错误节点。众所周知,排序的时间复杂度是O(nlogn),其中n为二叉搜索树中的节点个数。

我们能否省去这个排序过程,直接在原中序遍历所得序列上确定需要交换的两个错误节点呢?

对于一课正常二叉搜索树的中序遍历结果,一定是一个递增序列,比如[1, 2, 3, 4, 5]。如果我交换两个节点的位置,有两种情况,这两个节点相邻或者不相邻。

(1)如果被交换的两个节点是相邻的,假设我们交换2和3,那么新序列就是[1, 3, 2, 4, 5]。这时我们发现出现了一个降序[3, 2],这个降序中的两个节点就是错误节点。

(2)如果被交换的两个节点是不相邻,假设我们交换2和4,那么新序列就是[1, 4, 3, 2, 5]。这时我们发现出现了两个降序[4, 3]和[3, 2]。第一个降序中的前一个节点和第二个降序中的后一个节点就是错误节点。

省去了排序,因此时间复杂度和空间复杂度均是O(n)。

JAVA代码:

public class Solution {

    List<TreeNode> list;

    public void recoverTree(TreeNode root) {
        list = new ArrayList<>();
        inorderTraversal(root);
        List<Integer> wrongList = new ArrayList<>();
        int countDown = 0;
        for(int i = 1; i < list.size(); i++){
            if(list.get(i).val < list.get(i - 1).val){
                if(countDown == 0){
                    wrongList.add(i - 1);
                    wrongList.add(i);
                }
                wrongList.set(1, i);
                countDown++;
            }
        }

        change(list, wrongList.get(0), wrongList.get(1));
    }

    private void inorderTraversal(TreeNode root){
        if(root == null){
            return;
        }
        inorderTraversal(root.left);
        list.add(root);
        inorderTraversal(root.right);
    }

    private void change(List<TreeNode> list, int i, int j){
        Integer temp = list.get(i).val;
        list.get(i).val = list.get(j).val;
        list.get(j).val = temp;
    }
}

LeetCode解题报告:

思路三:思路二的改进

这个思路和LeetCode098——验证二叉搜索树中的思路三是一致的。

思路二把中序遍历的结果都保存在了一个List集合里,在遍历完成之后寻找降序序列。

事实上,我们在思路二中寻找降序序列的过程,其实只和中序遍历的当前节点和当前节点的前一个节点有关系,因此,我们完全可以在中序遍历的过程中就对结果进行验证。

这里我采用的是递归的形式实现中序遍历,更多中序遍历算法请见LeetCode094——二叉树的中序遍历

时间复杂度是O(n),其中n为树中的节点个数。空间复杂度即递归深度,是O(h),其中h为树的高度。

JAVA代码:

public class Solution {

    TreeNode wrongTreeNode1 = null;
    TreeNode wrongTreeNode2 = null;
    TreeNode pre = null;
    int countDown = 0;

    public void recoverTree(TreeNode root) {
        inorderTraversal(root);
        int temp = wrongTreeNode1.val;
        wrongTreeNode1.val = wrongTreeNode2.val;
        wrongTreeNode2.val = temp;
    }

    private void inorderTraversal(TreeNode root){
        if(root == null){
            return;
        }
        inorderTraversal(root.left);
        if(pre != null){
            if(root.val < pre.val){
                if(countDown == 0) {
                    wrongTreeNode1 = pre;
                }
                wrongTreeNode2 = root;
                countDown++;
            }
        }
        pre = root;
        inorderTraversal(root.right);
    }
}

LeetCode解题报告:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值