下面的代码将调用迭代器并将整数发送回调用它的方法。我只是想知道如果使用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();
}
答案 0 :(得分:3)
如果您创建的Iterator
直接从i
的{{1}}个索引处开始,则需要知道这也需要LinkedList
。在O(n)
中查找元素总是慢。
LinkedList
仅记住列表的LinkedList
(和head
)元素。需要通过遍历整个列表找到所有其他元素。
以下是双重链接列表的说明(Javas tail
也是双重链接):
因此,如果您从LinkedList
- 元素开始创建Iterator
,则会从i
(或head
)开始,并按照指向{ {1}} - 元素。这就像打电话:
tail
这显然要花费i
。
如果您需要快速基于索引的访问,也称为随机访问,您可以考虑使用list.get(i);
。它的结构允许在O(n)
中访问(它可以通过ArrayList
直接计算元素在内存中的位置。)
提供如此快速随机访问的数据结构通常将接口O(1)
(documentation and implementing classes)作为指标实现。
如上所述,迭代start + i * sizeof(type)
不应通过RandomAccess
通过基于索引的访问来完成。因此,如果您需要在迭代时修改列表,则应使用LinkedList
(或list.get(i)
。
以下是使用Iterator
的常用方法:
ListIterator
或者您也可以使用增强型for循环,它在内部也是如此,但看起来更紧凑:
Iterator
由于Javas Iterator<E> iter = list.iterator();
while (iter.hasNext()) {
E element = iter.next();
...
}
是双向链接列表,您也可以从尾部开始,然后迭代列表反向。因此,只需使用for (E element : list) {
...
}
方法(documentation)代替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
向前和向后遍历列表。这是documentation。
答案 1 :(得分:3)
如果您的目的是获取最后一个元素,那么由于LinkedList
实现是一个双向链接列表,那么应该有一个对实例可用的尾部和头部的引用,如以及大小。
这意味着List.descendingIterator()
可能是一个更好的起点,如果你必须从最后开始,但你总是在O(n)时间内索引到LinkedList
结构
如果您经常通过索引取消引用,则通常应使用随机访问结构,例如ArrayList
。
如果您实际上是在任一方向上遍历列表,那么您应该返回迭代器,而不是值。
答案 2 :(得分:2)
没有
链接列表不能在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
的增加而线性增加。