LeetCode 99 - Recover Binary Search Tree

LeetCode 99 - Recover Binary Search Tree

题目链接:99. Recover Binary Search Tree

传统 Stack + Iterative

传统的Recursive或者Stack + Iterative解法的空间复杂度都是O(n)。

class Solution {
    public void recoverTree(TreeNode root) {
        TreeNode first = null, second = null;
        TreeNode prev = new TreeNode(Integer.MIN_VALUE);
        Stack stack = new Stack<>();

        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            if (first == null && root.val < prev.val) {
                first = prev;
            }
            if (first != null && root.val < prev.val) {
                second = root;
            }
            prev = root;
            root = root.right;
        }

        int temp = first.val;
        first.val = second.val;
        second.val = temp;
    }
}

Morris Traversal

二叉树的前序(preorder)、中序(inorder)、后序(postorder)遍历有两个常用的方法:一是递归(recursive),二是使用栈实现的迭代版本(stack+iterative)。这两种方法都是O(n)的空间复杂度(递归本身占用stack空间或者用户自定义的stack).

本题的Follow-up挑战O(1)空间,要使用O(1)空间完成遍历,最大的难点在于,遍历到子节点的时候怎样重新返回到父节点,由于不能使用Stack作为辅助空间。为了解决这个问题,Morris Traversal使用了**线索二叉树(threaded binary tree)**的概念。在Morris Traversal中不需要为每个节点额外分配指针指向其前驱(predecessor)和后继节点(successor),只需要利用叶子节点的左右空指针指向某种顺序遍历下的前驱或后继节点就可以。

Morris 中序遍历步骤:

  1. 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
  2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。
  • 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。
  • 如果前驱节点的右孩子为当前节点,将它的右孩子重置为空(恢复树的形状)。输出当前节点,当前节点更新为当前节点的右孩子。
  1. 重复以上1、2步骤直至当前节点为空。

Morris中序遍历示例图如下(从左至右,从上到下):
LeetCode 99 - Recover Binary Search Tree_第1张图片
复杂度分析:
空间复杂度:O(1),只使用了两个辅助指针
时间复杂度:O(n),n个节点的二叉树中一共有n-1条边,整个过程中每条边最多只走2次,一次是为了定位到某个节点,另一次是为了寻找上面某个节点的前驱节点,如下图所示,其中红色是为了定位到某个节点,黑色线是为了找到前驱节点。所以复杂度为O(n)。
LeetCode 99 - Recover Binary Search Tree_第2张图片

本题Morris Traversal代码:

class Solution {
    public void recoverTree(TreeNode root) {
        TreeNode first = null, second = null;
        TreeNode preIn = new TreeNode(Integer.MIN_VALUE);

        while (root != null) {
            if (root.left == null) {
                if (root.val < preIn.val) {
                    if (first == null) {
                        first = preIn;
                        second = root;
                    } else {
                        second = root;
                    }
                }
                preIn = root;
                root = root.right;
            } else {
                TreeNode prev = root.left;
                while (prev.right != null && prev.right != root) {
                    prev = prev.right;
                }

                if (prev.right == null) {
                    prev.right = root;
                    root = root.left;
                } else {
                    if (root.val < preIn.val) {
                        if (first == null) {
                            first = preIn;
                            second = root;
                        } else {
                            second = root;
                        }
                    }
                    preIn = root;
                    prev.right = null;
                    root = root.right;
                }
            }
        }
        int temp = first.val;
        first.val = second.val;
        second.val = temp;
    }
}

Reference

  • Morris Traversal 方法遍历二叉树

你可能感兴趣的:(LeetCode)