簡體   English   中英

迭代器Java中LinkedList的時間復雜度?

[英]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();
}

迭代器從第i個元素開始

如果您創建一個直接從LinkedListi個索引處開始的Iterator ,您需要知道這也需要O(n) LinkedList查找元素總是很慢

LinkedList 只記憶列表head (和tail )元素。 遍歷整個列表需要找到每個其他元素。

這是一個雙向鏈表的例證(Javas LinkedList也是雙重鏈接):

LinkedList的結構

因此,如果從第i個元素開始創建一個Iterator ,它將從head (或tail )開始,並按照指針一直到第i個元素。 這就像打電話:

list.get(i);

這顯然花費了O(n)


替代方案:ArrayList

如果您需要基於索引的快速訪問 (也稱為隨機訪問) ,則可以考慮使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM