【Leecode】Leecode刷题之路第99天之恢复二叉搜索树

题目出处

99-恢复二叉搜索树-题目出处

题目描述


个人解法

思路:

java 复制代码
todo

代码示例:(Java)

java 复制代码
todo

复杂度分析

java 复制代码
todo

官方解法

99-恢复二叉搜索树-官方解法

方法1:显式中序遍历

思路:

代码示例:(Java)

java 复制代码
@Data
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;
    }
}

public class Solution1 {
    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 index1 = -1, index2 = -1;
        for (int i = 0; i < n - 1; ++i) {
            if (nums.get(i + 1) < nums.get(i)) {
                index2 = i + 1;
                if (index1 == -1) {
                    index1 = i;
                } else {
                    break;
                }
            }
        }
        int x = nums.get(index1), y = nums.get(index2);
        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);
        }
    }


}

复杂度分析

  • 时间复杂度:O(N),其中 N 为二叉搜索树的节点数。中序遍历需要 O(N) 的时间,判断两个交换节点在最好的情况下是 O(1),在最坏的情况下是 O(N),因此总时间复杂度为 O(N)。
  • 空间复杂度:O(N)。我们需要用 nums 数组存储树的中序遍历列表。

方法2:隐式中序遍历

思路:


代码示例:(Java)

java 复制代码
public class Solution2 {
    public void recoverTree(TreeNode root) {
        Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
        TreeNode x = null, y = null, pred = null;

        while (!stack.isEmpty() || root != null) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            if (pred != null && root.val < pred.val) {
                y = root;
                if (x == null) {
                    x = pred;
                } else {
                    break;
                }
            }
            pred = root;
            root = root.right;
        }

        swap(x, y);
    }

    public void swap(TreeNode x, TreeNode y) {
        int tmp = x.val;
        x.val = y.val;
        y.val = tmp;
    }


}

复杂度分析

  • 时间复杂度:最坏情况下(即待交换节点为二叉搜索树最右侧的叶子节点)我们需要遍历整棵树,时间复杂度为 O(N),其中 N 为二叉搜索树的节点个数。
  • 空间复杂度:O(H),其中 H 为二叉搜索树的高度。中序遍历的时候栈的深度取决于二叉搜索树的高度。

方法3:Morris 中序遍历

思路:


代码示例:(Java)

java 复制代码
public class Solution3 {
    public void recoverTree(TreeNode root) {
        TreeNode x = null, y = null, pred = null, predecessor = null;

        while (root != null) {
            if (root.left != null) {
                // predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
                predecessor = root.left;
                while (predecessor.right != null && predecessor.right != root) {
                    predecessor = predecessor.right;
                }

                // 让 predecessor 的右指针指向 root,继续遍历左子树
                if (predecessor.right == null) {
                    predecessor.right = root;
                    root = root.left;
                }
                // 说明左子树已经访问完了,我们需要断开链接
                else {
                    if (pred != null && root.val < pred.val) {
                        y = root;
                        if (x == null) {
                            x = pred;
                        }
                    }
                    pred = root;

                    predecessor.right = null;
                    root = root.right;
                }
            }
            // 如果没有左孩子,则直接访问右孩子
            else {
                if (pred != null && root.val < pred.val) {
                    y = root;
                    if (x == null) {
                        x = pred;
                    }
                }
                pred = root;
                root = root.right;
            }
        }
        swap(x, y);
    }

    public void swap(TreeNode x, TreeNode y) {
        int tmp = x.val;
        x.val = y.val;
        y.val = tmp;
    }


}

复杂度分析

  • 时间复杂度:O(N),其中 N 为二叉搜索树的高度。Morris 遍历中每个节点会被访问两次,因此总时间复杂度为 O(2N)=O(N)。
  • 空间复杂度:O(1)。

考察知识点

1.二叉树的中序遍历

收获

Gitee源码位置

99-恢复二叉搜索树-源码

相关推荐
牧子与羊几秒前
idea下java的maven项目编译内存溢出GC overhead limit exceeded解决办法
java·maven·intellij-idea
Fishel-14 分钟前
线性回归api再介绍
算法·回归·线性回归
勤劳的进取家23 分钟前
协方差矩阵
线性代数·算法·机器学习·矩阵
n北斗31 分钟前
arr.length 和 string.length()
java
野生派蒙1 小时前
排序算法:冒泡排序
java·数据结构·算法·排序算法
小万编程1 小时前
【2025最新计算机毕业设计】基于SpringBoot+Vue教研听课管理系统(高质量源码,提供文档,免费部署到本地)
java·spring boot·计算机毕业设计·java毕业设计·java毕设源码·web毕业设计
啾啾Fun1 小时前
[java基础]LinkedList源码粗析
java·开发语言
白露与泡影1 小时前
2025 年春招互联网大厂226 道 Java 高级岗面试题
java·开发语言
晨辉软件1 小时前
晨辉面试抽签和评分管理系统之三:考生批量抽签
算法·面试
学无止境_永不停歇1 小时前
5. 多线程(3) --- synchronized
java·java-ee