【问题标题】:What is the time complexity of printing all paths of a BST打印 BST 的所有路径的时间复杂度是多少
【发布时间】:2020-07-24 22:46:42
【问题描述】:

我有这段代码,它与本网站上具有相同问题的其他一些代码略有不同:

public void printAllRootToLeafPaths(Node node,ArrayList path) {
    if(node==null){
        return;
    }
    path.add(node.data);

    if(node.left==null && node.right==null)
    {
        System.out.println(path);
        return;
    }
    else {
        printAllRootToLeafPaths(node.left, new ArrayList(path));
        printAllRootToLeafPaths(node.right,new ArrayList(path));
    }      
}

有人可以解释为什么最好的情况是 O(nlogn) 吗? 最坏的情况,树分解成一个链表,数组被复制 1+2+3+...+n-1+n 次,相当于 n^2,所以时间复杂度是 O(n^2) .
沿着路径中的每个节点复制最佳情况数组。所以复制它看起来像 1+2+3+...+logn。 N/2 次。那么,如果 1+2+3+...+n-1+n 是 n^2,为什么总和 (logn)^2 不是呢?做最好的情况 O(n(logn)^2)?

【问题讨论】:

  • 这段代码略有不同,因为每次调用方法都会复制数组。我的印象是复制数组会增加时间复杂度。也就是说,复制数组的成本 O(l) l 是数组的长度。因此对于每个路径中的每个节点,复制数组看起来像 1+2+3+...+logn。但是,似乎代码,添加整个数组一次的行为是 O(logn) (或者如果它被打印,它将是 O(logn)。所以我很困惑。是否为每个调用复制数组不会添加到时间复杂度?打印会增加时间复杂度吗?

标签: java algorithm recursion


【解决方案1】:

是的,所有的复制都需要考虑在内。

那些副本不是必需的;编写以下函数很容易:

  • 使用(单)链表代替 ArrayList,或者
  • 使用单个 ArrayList,在不再需要时覆盖以前的路径。

对于非复制算法,算法的成本是打印路径的成本,它是从根到叶的所有路径长度的总和。在每次调用时复制数组,使成本成为从根到每个节点的路径的总和,而不是每个叶子。 (数组被复制两次其实和复杂度分析无关,只是乘以一个常数因子,可以忽略。)

使用非复制算法,最好的情况是一棵线性树,每个节点只有一个孩子。那么只有一个叶子的路径长度为N,所以总成本是O(N)。但如果你在每个节点上复制,那是最坏的输入;节点路径长度为连续整数,节点路径长度之和为二次方。

对于您的算法,最好的情况是完全平衡的完全占用树。在完全占用的树中,叶子节点比非叶子节点多一个;换句话说,大约一半的节点是叶子。在完美平衡的树中,每个节点都可以在最多 log N 步内从根到达。所以到每个节点的路径长度总和是 O(N log N)。 (有些节点更接近,但是对于计算大 O,我们可以忽略这个事实。即使我们将其考虑在内,我们也会发现它不会改变渐近行为,因为每个深度的节点数每个连续级别的级别加倍。)因为一半的节点是叶子,所以这个输入的成本是 O(N log N) 与非复制算法。

两种算法都表现出最坏情况的二次复杂度。我们已经看到了复制算法的最坏情况输入:只有一个叶子的线性树。对于非复制算法,我们使用非常相似的东西:由左链接主干组成的树,其中每个右链接都是叶子:

                         root
                          /\
                         /  \
                        /\   7
                       /  \
                      /\   6
                     /  \
                    /\   5
                   /  \
                  /\   4
                 /  \
                /\   3
               /  \
               1  2

由于那棵树被完全占用,它的一半节点是叶子,并且它们的距离连续增加,所以这又是二次的(尽管常数乘数更小。)

【讨论】:

    【解决方案2】:

    我认为您错过了将整个数组复制到新数组的时间复杂度。


    在根节点:one element is added, but 2 new arrays are created

    根节点的左子节点:one element is added, but 2 new arrays are created with 2 elements

    。 . .

    最后只有一层:one element is added, but 2 new arrays are created of size log n to base 2.

    在最后一级:添加一个元素,并打印 log n 个元素。


    因此,在每个节点处,都有一个将元素添加到列表的操作,以及一个打印或复制大小列表 (log h) 的操作 - 其中 h 是树中节点的高度。

    由于我们只遍历所有元素一次,因此操作总数为:n additions + Sum (log h1 + log h2 + log h3 + .... log hn)

    其中 h1, h2, h3...hn 是每个节点的高度。

    大约是n + O(n log n)~O(n log n)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      • 2018-11-24
      • 2014-05-27
      相关资源
      最近更新 更多