[英]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.