1. 程式人生 > >ArrayList get() add() remove()方法LinkedList get() add() remove()方法的時間複雜度分別是多少?

ArrayList get() add() remove()方法LinkedList get() add() remove()方法的時間複雜度分別是多少?

ArrayList 是線性表(陣列)get() 直接讀取第幾個下標,複雜度 O(1)add(E) 新增元素,直接在後面新增,複雜度O(1)add(index, E) 新增元素,在第幾個元素後面插入,後面的元素需要向後移動,複雜度O(n)remove()刪除元素,後面的元素需要逐個移動,複雜度O(n)LinkedList 是連結串列的操作get() 獲取第幾個元素,依次遍歷,複雜度O(n)add(E) 新增到末尾,複雜度O(1)add(index, E) 新增第幾個元素後,需要先查詢到第幾個元素,直接指標指向操作,複雜度O(n)remove()刪除元素,直接指標指向操作,複雜度O(1)

我們知道,連結串列和陣列相比,最主要的特點就是add和remove的操作是O(1)的。Java中的連結串列一般使用LinkedList這個型別,陣列一般使用ArrayList。它們同時implements了List這個interface,所以都有remove(int index)和remove(Object o)這兩個方法。

普通意義上認為連結串列的remove操作是O(1)的,是因為對於某個給定的節點node,可以將它的前置節點的next直接置為node的下一個。而陣列,則需要刪除index處的元素,再將後面n個元素前移,所以需要O(n)的時間。

但是,在Java中果真如此嗎?

細看JDK的原始碼,就可以發現,LinkedList的remove(int index)和remove(Object o)這兩個方法都做不到O(1)的時間,而是O(n)。這是因為上面說的資料結構中的O(1)時間,是對於某個已經確定的節點。而LinkedList中,首先必須通過一個迴圈,找到第一個出現的Object o,或者走到index這個位置,再進行操作。也就是,有一個get的過程。

這時,雖然ArrayList的remove(int index)和remove(Object o)也是O(n)時間,但是移動耗費的時間遠比LinkedList中往後定址來的快得多,特別是元素很多的時候。JDK的原始碼裡,這個操作是用System.arraycopy()來做的。所以,這時,LinkedList的最為坑爹的地方,也是最令人不解的地方就出現了——remove的操作居然比ArrayList還慢,而且慢的多!

而且,LinkedList需要內部維護一個數據結構,JDK 6中叫Entry,JDK 7中叫Node,這需要很多額外的記憶體。所以,除非急切需要LinkedList的Deque功能,任何情況下都應該使用ArrayList。其實,即使要用Deque,也有ArrayDeque。

所以,有時面試會問,在一個LinkedList list的遍歷for迴圈中,不斷執行remove(i)操作,時間複雜度是多少?其實是O(n^2),而不是O(n)。但是,如果使用iterator,it.remove()的時間複雜度就是O(1)了,因為這時元素已經給定。並且,for迴圈中進行remove(i)操作是要影響下標的。remove過後每次i都必須i--。使用iterator可以有效避免這個問題。這裡可以看到,雖然for迴圈比較直觀,但是有時iterator還是非常好的。