【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-恢复二叉搜索树-源码

相关推荐
郝学胜-神的一滴29 分钟前
中秋特别篇:使用QtOpenGL和着色器绘制星空与满月
开发语言·c++·算法·软件工程·着色器·中秋
半旧夜夏1 小时前
【Spring】AOP的核心原理配方
java·spring
qiuiuiu4131 小时前
CPrimer Plus第十六章C预处理器和C库总结2-qsort函数
java·c语言·算法
IT学长编程1 小时前
计算机毕设选题 基于SpringBoot的书店管理系统的设计与实现 网上书店系统 前后端分离 Java毕设项目 毕业设计选题 【附源码+文档报告+安装调试】
java·spring boot·毕业设计·课程设计·前后端分离·网上书店系统·书店管理系统
张较瘦_1 小时前
应用型本科计算机类专业毕业设计与论文选题指南
java·开发语言·课程设计
IT学长编程1 小时前
计算机毕设选题 基于SpringBoot的房产租赁管理系统 房屋租赁系统 前后端分离 Java毕设项目 毕业设计选题 【附源码+文档报告+安装调试】
java·spring boot·毕业设计·课程设计·房屋租赁系统·房产租赁系统·文档报告
JuneXcy2 小时前
C++知识点总结用于打算法
c++·算法·图论
木易 士心3 小时前
MPAndroidChart 用法解析和性能优化 - Kotlin & Java 双版本
android·java·kotlin