關於數組為什么要1.5倍或者2倍擴容


一、為什么不是常數擴容而是成倍擴容

首先我們要明確數組是一塊連續的內存,在添加元素的過程中,如果我們的數組存不下了,則需要開辟一塊新的內存,把原來的元素復制到新開辟的地方,具體過程如下:

  1. 新開辟(allocate)足夠大小的內存
  2. 把舊元素復制到新的內存中
  3. 釋放(deallocate)原來的內存

其中第二步需要的時間復雜度為O(n),這樣我們有一個時間和空間的tradeoff,就是說如果我們新開辟的內存很大,一次開辟可以存很多新元素進去,我們reallocation、復制的次數會少,但是很可能會浪費很多空間;如果每次新開辟只比原來的內存大一點,空間浪費很少,不過reallocation、復制的次數會很多。

下面我們比較一下常數擴容和成倍擴容的時間復雜度:

1. 選擇常數擴容,即每次擴容的空間比原來大c

假如說初始數組有1個元素,每次擴容空間增加1,最終擴容成容納n個元素,則每次擴容,復制舊元素和加入新元素,添加元素(append)的次數如下:

	擴容次數      數組長度     append次數
	   0            1             1 (復制0個,新加入1個)
	   1            2             2 (復制1個,新加入1個)
	   2            3             3 (復制2個,新加入1個)
	  ...          ...           ...
	  n-1           n             n

共計 1 + 2 + 3 + ... + n = \(O(n^2)\),均攤到每個元素則是\(O(n)\)。(或者可以這樣理解,到最后n個元素中,第一個元素(最老的元素)被append了n次,第二個元素被append了n-1次,以此類推,最后一個元素被append了1次)

擴容c個只有字母前面倍數的差別,均攤還是\(O(n)\)

2. 選擇成倍擴容

每次擴容2倍,最終擴容成容納n個元素,則復制的次數如圖:
image

圖中是被copy的次數,我們這里算被append的次數(被copy的次數加1):

最終數組里的n個元素中,n個元素append次數至少是1,n/2個元素append次數至少是2,n/4個元素append次數至少是3,以此類推,則共計 n + n/2 + n/4 + n/8 + ... = \(O(n)\),均攤到每個元素是\(O(1)\)

(解釋一下這個式子,以第二項為例,n/2個元素是包含在n個元素里的,這n/2個元素的1次append已經在第一項里算過一遍了,所以每一項不用乘被append次數)

常數擴容均攤的時間復雜度為\(O(n)\),根據時間復雜度的比較,我們選擇成倍擴容


二、為什么選擇2倍擴容或1.5倍擴容?

不選擇更大倍數的擴容是為了避免浪費更多空間

選擇1.5倍擴容還有一個好處,就是可以使用前面釋放的空間,如圖所示:
image


第一部分來自 https://www.drdobbs.com/c-made-easier-how-vectors-grow/184401375 解釋了為什么不用常數擴容
第二部分來自 https://blog.csdn.net/qq_44918090/article/details/120583540 面試題:C++vector的動態擴容,為何是1.5倍或者是2倍

第二個鏈接關於擴容問題寫的很全面


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM