Java 为什么树集迭代是O(n)而不是O(n*logn)?

Java 为什么树集迭代是O(n)而不是O(n*logn)?,java,algorithm,data-structures,tree,treeset,Java,Algorithm,Data Structures,Tree,Treeset,我读了一篇关于TreeSet的时间复杂性的文章,答案是它需要O(n)个时间。然而,我不明白为什么迭代是O(n)而不是O(n*nlogn) 下一个电话都需要时间 因此,如果我遍历这样的树集: while (iterator.hasNext()){ //Runs N times System.out.println(iterator.next() + " "); //each next is O(logn) } void print(Node n) { if (n.left != nu

我读了一篇关于TreeSet的时间复杂性的文章,答案是它需要O(n)个时间。然而,我不明白为什么迭代是O(n)而不是O(n*nlogn)

下一个电话都需要时间

因此,如果我遍历这样的树集:

while (iterator.hasNext()){ //Runs N times
   System.out.println(iterator.next() + " "); //each next is O(logn)
}
void print(Node n) {
   if (n.left != null) print(n.left);
   System.out.println(n.value);
   if (n.right != null) print(n.right);
}

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

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

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

while (iterator.hasNext()){ //Runs N times
   System.out.println(iterator.next() + " "); //each next is O(logn)
}
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()
时前进。确实,
println
s之间的函数调用数量是不均匀的,但是当你整体观察时,你会发现有N个函数调用。

为什么
iterator.next()
是O(log N)。它只需要转到下一个节点,这是O(1),不是吗?@joselu从源代码来看不准确。@louis wasserman你说得对,我很抱歉。我认为迭代器()可以返回一个包含已排序节点的列表,然后转到下一个节点是很容易的。迭代器.next的代码似乎最终出现在
TreeMap.Entry.succession()
,这里:是的,似乎大多数时候下一个条目都是
p.left!=空
。然而,Big-O不应该是最坏的情况,而不是平均情况吗?@markspace Big-O描述了你想要它做的任何行为,因为它本身与复杂性无关,它与函数的增长有关。你可以用很高的概率来描述平均成本、最坏情况、摊销成本、预期成本……在这种情况下,所有操作的θ(n)(最坏和最佳情况)都是偶数。查找单个后继函数可能需要花费log n时间,但迭代所有n个元素最多需要2n。@markspace没有细化的big-O界限指的是最坏情况下的运行时间,但big-O表示法只是一个关于函数的数学语句。@markspace是的,每个
迭代器。next()
调用都有最坏情况下的时间O(log n)。但同时,遍历整个
树集
的最坏情况时间为O(n)。(在上面给出的答案中,如果您在整个
TreeSet
上迭代,那么在最坏的情况下,
iterator.next()
的平均时间将是O(1)。)