【问题标题】:Why is TreeSet Iteration O(n) instead of O(n*logn)?为什么 TreeSet 迭代 O(n) 而不是 O(n*logn)?
【发布时间】:2016-04-14 20:48:58
【问题描述】:

我阅读了 previous question 关于 TreeSet 时间复杂度的文章,答案是它需要 O(n) 时间。但是,我不明白为什么迭代是 O(n) 而不是 O(n*nlogn)。

下一个电话需要O(logn) time

所以如果我像这样遍历 TreeSet:

while (iterator.hasNext()){ //Runs N times
   System.out.println(iterator.next() + " "); //each next is O(logn)
}

我希望它是 O(n*logn) 而不是 O(n),因为 while 循环有 N 次迭代,并且每个 iterator.next() 调用都需要 O(logn) 时间。

【问题讨论】:

  • 为什么iterator.next() 是 O(log n)。它只需要转到下一个节点,这是 O(1),不是吗?
  • @JoseLuis 不准确,基于查看源代码。
  • @louis-wasserman 你说得对,我很抱歉。我认为 iterator() 可以返回一个带有排序节点的列表,然后去下一个节点很容易。

标签: java algorithm data-structures tree treeset


【解决方案1】:

next 操作的最坏情况时间是O(log n),因为那是树的高度。然而,平均而言,下一个元素可以及时找到O(1)。这是因为整个遍历本质上使用了每个 n-1 树边两次。

【讨论】:

  • Iterator.next 的代码似乎以TreeMap.Entry.successor() 结尾,这里是:grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… 是的,似乎大多数时候下一个条目只是p.left != null。但是,Big-O 不应该是最坏的情况,而不是平均情况吗?
  • @markspace Big-O 描述了您想要的任何行为,因为它本身与复杂性无关,而与功能的增长有关。您可以以高概率描述平均、最坏情况、摊销、预期成本,......在这种情况下,它甚至是所有操作的 Theta(n)(最坏和最好情况)。找到一个后继可能会花费 log n 时间,但迭代所有 n 个元素最多花费 2n。
  • @markspace A big-O bound 没有详细说明是指最坏情况下的运行时间,但 big-O 表示法只是关于函数的数学陈述。
  • @markspace 是的,每个iterator.next() 调用都有最坏情况时间 O(log n)。但同时,遍历整个TreeSet 的最坏情况时间为 O(n)。 (在上面给出的答案中,如果您要遍历整个 TreeSet,那么在最坏的情况下,iterator.next() 的平均时间将为 O(1)。)
【解决方案2】:

您可以像这样为树实现迭代:

void print(Node n) {
   if (n.left != null) print(n.left);
   System.out.println(n.value);
   if (n.right != null) print(n.right);
}

函数 print 将对每个节点只调用一次,因此总迭代时间为 O(N)。您可以迭代地实现完全相同的算法(无需递归)。如果你足够小心,你可以有一个类来保持迭代状态并在调用.next() 时前进。 printlns 之间的函数调用次数确实是参差不齐的,但是整体来看你会发现正好有 N 个。

【讨论】:

    猜你喜欢
    • 2011-10-22
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2018-08-05
    相关资源
    最近更新 更多