空間複雜度有多複雜?

在這裏插入圖片描述

1. 什麼是空間複雜度

在這裏插入圖片描述
在運行一段程序時,我們不僅要執行各種運算指令,同時也會根據需要,存儲一些臨時的中間數據 ,以便後續指令可以更方便地繼續執行。

在什麼情況下需要這些中間數據呢?讓我們來看看下面的例子。

給出下圖所示的n個整數,其中有兩個整數是重複的,要求找出這兩個重複的整數。

在這裏插入圖片描述
對於這個簡單的需求,可以用很多種思路來解決,其中最樸素的方法就是雙重循環,具體如下。

遍歷整個數列,每遍歷到一個新的整數就開始回顧之前遍歷過的所有整數,看看這些整數裏有沒有與之數值相同的。

第1步,遍歷整數3,前面沒有數字,所以無須回顧比較。

第2步,遍歷整數1,回顧前面的數字3,沒有發現重複數字。

第3步,遍歷整數2,回顧前面的數字3、1,沒有發現重複數字。

在這裏插入圖片描述

後續步驟類似,一直遍歷到最後的整數2,發現和前面的整數2重複。

在這裏插入圖片描述
雙重循環雖然可以得到最終結果,但它顯然並不是一個好的算法。

它的時間複雜度是多少呢?

我們不難得出結論,這個算法的時間複雜度是O(n2) 。

在這裏插入圖片描述
如何利用中間數據呢?

當遍歷整個數列時,每遍歷一個整數,就把該整數存儲起來,就像放到字典中一樣。當遍歷下一個整數時,不必再慢慢向前回溯比較,而直接去“字典”中查找,看看有沒有對應的整數即可。

假如已經遍歷了數列的前7個整數,那麼字典裏存儲的信息如下。

在這裏插入圖片描述

“字典”左側的Key代表整數的值,“字典”右側的Value代表該整數出現的次數(也可以只記錄Key)。

接下來,當遍歷到最後一個整數2時,從“字典”中可以輕鬆找到2曾經出現過,問題也就迎刃而解了。

在這裏插入圖片描述
由於讀寫“字典”本身的時間複雜度是O(1) ,所以整個算法的時間複雜度是O(n) ,和最初的雙重循環相比,運行效率大大提高了。

而這個所謂的“字典”,是一種特殊的數據結構,叫作散列表 。這個數據結構需要開闢一定的內存空間來存儲有用的數據信息。

但是,內存空間是有限的,在時間複雜度相同的情況下,算法佔用的內存空間自然是越小越好。如何描述一個算法佔用的內存空間的大小呢?這就用到了算法的另一個重要指標——空間複雜度(space complexity)。

2. 空間複雜度的計算

在這裏插入圖片描述

常見的空間複雜度有下面幾種情形。

在這裏插入圖片描述這回不弄喫的栗子啦~

1. 常量空間

當算法的存儲空間大小固定,和輸入規模沒有直接的關係時,空間複雜度記作O(1) 。

例如下面這段程序:

inline void fun1(int n){
	int num = 3;}

2.線性空間
當算法分配的空間是一個線性的集合(如數組),並且集合大小和輸入規模n成正比時,空間複雜度記作O(n) 。

例如下面這段程序:

inline void fun2(int n){
	int arr[n];}

3. 二維空間

當算法分配的空間是一個二維數組集合,並且集合的長度和寬度都與輸入規模n成正比時,空間複雜度記作O(n 2 ) 。

例如下面這段程序:

inline void fun3(int n){
	int arr[n][n];}

4. 遞歸空間

遞歸是一個比較特殊的場景。雖然遞歸代碼中並沒有顯式地聲明變量或集合,但是計算機在執行程序時,會專門分配一塊內存,用來存儲“調用棧”。

當函數返回時,執行出棧操作,把調用的信息從棧中彈出。

下面這段程序是一個標準的遞歸程序:

inline void fun4(int n){
	if(n <= 1) return;
	f(n - 1);}

假如初始傳入參數值n=5,那麼方法fun4(參數n=5)的調用信息先入棧。

在這裏插入圖片描述
接下來遞歸調用相同的方法,方法fun4(參數n=4)的調用信息入棧。
在這裏插入圖片描述
以此類推,遞歸越來越深,入棧的元素就越來越多。
在這裏插入圖片描述
當n=1時,達到遞歸結束條件,執行return指令,進行出棧。
在這裏插入圖片描述
最終,“調用棧”的全部元素會一一出棧。

由上面“調用棧”的出入棧過程可以看出,執行遞歸操作所需要的內存空間遞歸的深度。純粹的遞歸操作的空間複雜度也是線性的,如果遞歸的深度是n,那麼空間複雜度就是O(n) 。

3. 時間與空間的取捨

人們之所以花大力氣去評估算法的時間複雜度和空間複雜度,其根本原因是計算機的運算速度和空間資源是有限的。

就如一個大財主,基本不必爲日常花銷傷腦筋;而一個沒多少積蓄的普通人,則不得不爲日常花銷精打細算。

對於計算機系統來說也是如此。雖然目前計算機的CPU處理速度不斷飆升,內存和硬盤空間也越來越大,但是面對龐大而複雜的數據和業務,我們仍然要精打細算,選擇最有效的利用方式。

但是,正所謂魚和熊掌不可兼得。很多時候,我們不得不在時間複雜度和空間複雜度之間進行取捨。

👇雙重循環的時間複雜度是O(n2),空間複雜度是O(1),這就屬於犧牲時間來換取空間的情況。

👇相反,字典法的空間複雜度是O(n),時間複雜度是O(n),這屬於犧牲空間來換取時間 的情況。

但在絕大多數時候,時間複雜度更爲重要一些,我們寧可多分配一些內存空間,也要提升程序的執行速度。

時間複雜度&空間複雜度の小結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章