簡體   English   中英

ArrayList add(Type value) 方法 O(1) 如何攤銷時間復雜度?

[英]How is the ArrayList add(Type value) method O(1) amortized time complexity?

ArrayList 的大多數實現在內部使用數組,並且當在向列表中添加元素時大小已經用完時,它通過基本上執行以下操作來調整大小或“增長”:

  • 用一批新分配的內存緩存一個新數組。
  • 將內部數組的所有元素復制到新數組中。
  • 將內部數組設置為新數組。
  • 將內部數組的索引N - 1設置為元素對象,其中N是數組的新大小。

提供的解釋是,增加列表對於您的平均添加操作來說是一種罕見的必要性,因此平均添加的時間復雜度為O(1) ,因此攤銷常數時間。

我很困惑這是如何理解的。 假設列表增長Q 簡單的算術級數將告訴您,如果我將x元素添加到 ArrayList 中,如果xQ大幾倍,則內部完成的元素副本總數為x^2 + Qx / 2Q

當然,對於添加的前幾個值,時間很可能是恆定的,但是對於添加的足夠多的元素,我們看到每個添加操作的平均時間復雜度為線性或O(n) 因此,向列表中添加大量元素需要指數時間。 我不明白即使是單個添加操作的攤銷時間復雜度也是恆定的。 有什么我想念的嗎?

編輯:我沒有意識到列表增長實際上是幾何的,這優化了攤銷時間復雜度。

結論:

動態列表的線性增長

N = kQ

對於N + 1插入

副本:

  Q + 2Q + 3Q + … + kQ
= (k / 2)(2Q + (k - 1)Q)
= (k / 2)(Q + kQ) 
= (kQ + k^2 * Q) / 2 
-> kQ + k^2 * Q

元素初始化:

  Q + 2Q + 3Q + 4Q + … + (k + 1) * Q 
= ((k + 1) / 2)(2Q + kQ) 
= (k^2 * Q + 2kQ + 2Q + kQ) / 2 
-> k^2 * Q + 3kQ + 2Q

廉價插入:

  kQ + 1 
-> kQ

總成本: 2Q * k^2 + 5kQ + 2Q

每次插入的攤銷成本:

  2k + 5 + 2 / k 
-> 2k + 2 / k
-> O(N / Q)
-> O(N)

動態列表的幾何增長

N = Q^k

對於N + 1插入

副本:

  1 + Q + Q^2 + … +  Q^k 
= (1 - Q^(k + 1)) / (1 - Q) 
-> Q^k

元素初始化:

  1 + Q + Q^2 + … + Q^(k + 1) 
= (1 - Q^(k + 2)) / (1 - Q) 
-> Q^(k + 1)

廉價插入:

  Q^k + 1 
-> Q^k

總成本: 2Q^k + Q^(k + 1)

每次插入的攤銷成本:

  2 + Q
-> O(1)

比較

數組的幾何大小調整/增長是恆定時間,而線性調整大小是線性時間。 比較兩種增長方法以查看性能差異以及為什么選擇 ArrayList 以幾何方式增長是很有趣的。

不失一般性,假設列表的初始容量為 1。我們進一步假設每次插入超過容量時容量加倍。 現在考慮插入2^k + 1元素(這是最壞的情況,因為最后一個操作觸發了動態增長)。

k插入觸發動態增長,它們的累積成本為

1 + 2 + 4 + 8 + ... + 2^k = 2^(k+1) - 1

其他“廉價”插入的累積成本是2^k - k + 1

但是我們對攤銷復雜度感興趣,因此我們必須對所有2^k + 1操作求平均值:

  (2^(k+1) + 2^k - k) / (2^k + 1)
< (2^(k+1) + 2^k - k) / 2^k
= 2 + 1 - k/2^k
= O(1)

因此,向列表中插入2^(k+1)元素具有每次插入攤銷時間復雜度為 O(1),並且常數因子接近 3。向列表中插入任何其他數量的元素都不會更糟,因此攤銷的時間復雜度為每次插入的時間復雜度通常為 O(1)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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