修改结构

很符合递归思想的写法,美中不足是每次都要把左子树遍历一遍好去找尾端节点,如果整个树左子树体积非常大而右子树很小的话,时间复杂度会很高。最差的情况是,整个树是一个只有左边的链表,时间复杂度可以达到 O(n^2),而且用递归还要花费栈空间。

public class Solution {
    public void flatten(TreeNode root) {
        if(root == null) return;

        flatten(root.left);
        flatten(root.right);

        TreeNode left = root.left;
        TreeNode right = root.right;

        root.left = null;
        root.right = left;
        while(root.right != null){
            root = root.right;
        }
        root.right = right;
    }
}

于是这题有特别赞的O(n)时间O(1)空间写法,还是利用 Morris 遍历。

Morris 遍历的特点是寻找左子树中能沿着右边走最长的节点,并且利用这个节点做文章;这题是把 right 指针直接指向 root 的右节点了,相当于每次缩进去【root.left -> 左子树最长向右路径】这段到右子树上,如此反复。因此省去了最坏情况下重复遍历寻找链表尾的过程。

链表经典水题,把它放在这是因为它和下一题真的很像,只是维度上更单一而已。

这题与其说是 Tree 类题目,不如说更像 LinkedList... 因为有很多的存 temp 和改动 reference ptr 的过程,尤其是迭代版,活脱一个反转列表。

  • 迭代版

  • 递归版

LC 论坛的讨论帖 http://articles.leetcode.com/convert-binary-search-tree-bst-to/

递归

  • 完全就是 in - order 的递归结构,左-中-右

  • 核心在于 “中” 这步上,如何正确做好 “拼接” 工作

  • 我们需要存一个全局变量 prev 用于保存 "左子树的最后一个节点",在每步上,和 root 做双向拼接; prev 初始化为 null;

  • 额外用于遍历 LinkedList 还需要存下 head ; 在 prev 为 null 的时候 root 就代表着最左面的节点,设一下就好,之后就不用管了。

时间复杂度 O(n).

迭代(Stack)

  • In-order 跑一遍,每次 pop 出来的时候,我们就有 root 了;

  • 然后拼接的逻辑处理和递归的方法完全一样,这次连全局变量都不用,简单直接~

时间O(n),空间 O(log n)

Last updated

Was this helpful?