[英]Iterator Time complexity for a LinkedList in Java?
下面的代码将调用迭代器并将整数发送回调用它的方法。 我只想知道通过使用Iterator
是否会使我的时间复杂度变得不变? (即O(1))。 因为如果我使用带有LinkedList
的get,它将给出线性时间(即O(n))。
protected int least(int i) {
if(i >= digits.size()) {
return 0;
}
// temp++;
// System.out.println("Time taken=" + temp);
// long start = System.nanoTime();
ListIterator<Integer> itr1 = digits.listIterator(digits.size() - i);
// System.out.println("Time elapsed:" + (System.nanoTime() - start));
return itr1.previous();
}
如果您创建一个直接从LinkedList
第i
个索引处开始的Iterator
,您需要知道这也需要O(n)
。 在LinkedList
查找元素总是很慢 。
LinkedList
只记忆列表的head
(和tail
)元素。 遍历整个列表需要找到每个其他元素。
这是一个双向链表的例证(Javas LinkedList
也是双重链接):
因此,如果从第i
个元素开始创建一个Iterator
,它将从head
(或tail
)开始,并按照指针一直到第i
个元素。 这就像打电话:
list.get(i);
这显然花费了O(n)
。
如果您需要基于索引的快速访问 (也称为随机访问) ,则可以考虑使用ArrayList
。 它的结构允许在O(1)
访问(它可以通过start + i * sizeof(type)
直接计算元素在内存中的位置)。
提供这种快速随机访问的数据结构通常将接口RandomAccess
( 文档和实现类 ) 实现为指示符。
如上所述,迭代LinkedList
应该不是通过list.get(i)
通过基于索引的访问来完成的。 因此,如果需要在迭代时修改列表,则应使用Iterator
(或ListIterator
)。
以下是使用Iterator
的常用方法:
Iterator<E> iter = list.iterator();
while (iter.hasNext()) {
E element = iter.next();
...
}
或者你也可以使用增强的for循环 ,它在内部做同样的,但看起来更紧凑:
for (E element : list) {
...
}
由于Javas LinkedList
是一个双向链表,您也可以从尾部开始并反向迭代列表。 因此,只需使用LinkedList#descendingIterator
方法( 文档 )而不是LinkedList#iterator
。
最后一个示例演示如何在迭代时使用ListIterator
修改列表:
ListIterator<E> listIter = list.listIterator(0);
while (listIter.hasNext()) {
E element = listIter.next();
...
// Remove the element last polled
listIter.remove();
// Inserts an element right before the last polled element
listIter.add(new Element());
}
您也可以向前和向后遍历与列表中ListIterator
使用hasPrevious()
和previous()
方法。 这是它的文档 。
没有。
链接列表不能在O(1)
时间内随机访问。 使用迭代器来访问它仍然是O(n)
。
要理解为什么,我们需要深入研究listIterator(int)
:
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
这个方法返回一个新的ListItr
,让我们看看它是如何创建的:
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
构造函数调用node
,其实现方式如下:
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++) <<<<< HERE!
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--) <<<<<< HERE!
x = x.prev;
return x;
}
}
你看到了吗? for循环遍历节点一直到索引index
处的循环。 这意味着它是O(n)
! 获取迭代器的速度随着链表中的元素和index
增加而线性增加。
如果你的目的是获取last-one-one元素,那么由于LinkedList
实现是一个双向链表,那么应该有一个对实例可用的尾部和头部的引用,以及大小。
这意味着List.descendingIterator()
可能是一个更好的起点,如果你必须从最后开始,但你总是要在O(n)时间内索引到LinkedList
结构。
如果您经常通过索引解除引用,则通常应使用随机访问结构,例如ArrayList
。
如果您实际上是在任一方向上遍历列表,那么您应该返回迭代器,而不是值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.