在執行一段程式時,我們不僅要執行各種運算指令,同時也會根據需要,儲存一些臨時的中間資料 ,以便後續指令可以更方便地繼續執行。
在什麼情況下需要這些中間資料呢?讓我們來看看下面的例子。
給出下圖所示的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)。
常見的空間複雜度有下面幾種情形。
1. 常量空間
當演算法的儲存空間大小固定,和輸入規模沒有直接的關係時,空間複雜度記作o(1) 。
例如下面這段程式:
inline
void
fun1
(int n)
2.線性空間當演算法分配的空間是乙個線性的集合(如陣列),並且集合大小和輸入規模n成正比時,空間複雜度記作o(n) 。
例如下面這段程式:
inline
void
fun2
(int n)
3. 二維空間當演算法分配的空間是乙個二維陣列集合,並且集合的長度和寬度都與輸入規模n成正比時,空間複雜度記作o(n 2 ) 。
例如下面這段程式:
inline
void
fun3
(int n)
4. 遞迴空間遞迴是乙個比較特殊的場景。雖然遞迴**中並沒有顯式地宣告變數或集合,但是計算機在執行程式時,會專門分配一塊記憶體,用來儲存「呼叫棧」。
當函式返回時,執行出棧操作,把呼叫的資訊從棧中彈出。
下面這段程式是乙個標準的遞迴程式:
inline
void
fun4
(int n)
假如初始傳入引數值n=5,那麼方法fun4(引數n=5)的呼叫資訊先入棧。
接下來遞迴呼叫相同的方法,方法fun4(引數n=4)的呼叫資訊入棧。
以此類推,遞迴越來越深,入棧的元素就越來越多。
當n=1時,達到遞迴結束條件,執行return指令,進行出棧。
最終,「呼叫棧」的全部元素會一一出棧。
由上面「呼叫棧」的出入棧過程可以看出,執行遞迴操作所需要的記憶體空間∝遞迴的深度。純粹的遞迴操作的空間複雜度也是線性的,如果遞迴的深度是n,那麼空間複雜度就是o(n) 。
人們之所以花大力氣去評估演算法的時間複雜度和空間複雜度,其根本原因是計算機的運算速度和空間資源是有限的。
就如乙個大財主,基本不必為日常花銷傷腦筋;而乙個沒多少積蓄的普通人,則不得不為日常花銷精打細算。
對於計算機系統來說也是如此。雖然目前計算機的cpu處理速度不斷飆公升,記憶體和硬碟空間也越來越大,但是面對龐大而複雜的資料和業務,我們仍然要精打細算,選擇最有效的利用方式。
但是,正所謂魚和熊掌不可兼得。很多時候,我們不得不在時間複雜度和空間複雜度之間進行取捨。
?雙重迴圈的時間複雜度是o(n2),空間複雜度是o(1),這就屬於犧牲時間來換取空間的情況。
?相反,字典法的空間複雜度是o(n),時間複雜度是o(n),這屬於犧牲空間來換取時間 的情況。
但在絕大多數時候,時間複雜度更為重要一些,我們寧可多分配一些記憶體空間,也要提公升程式的執行速度。
複雜度分析 時間複雜度 空間複雜度
執行效率是演算法的乙個重要的考量指標,演算法的執行效率用時間 空間複雜度來衡量。今天我們來學習一下複雜度的分析。通常我們可以通過執行程式來獲得演算法的真正的執行時間,這種方法我們可以稱為事後統計法,但這種方法得到的是具體的資料,測試結果很依賴測試環境,而且受資料規模影像最大。因此,我們需要乙個不需要...
時間複雜度 空間複雜度
時間複雜度 在電腦科學中,演算法的時間複雜度是乙個函式,它定性描述了該演算法的執行時間。這是乙個關於代表演算法輸入值的字串 的長度的函式。時間複雜度常用大o符號 表述,不包括這個函式的低階項和首項係數。計算時間複雜度的方法 1 只保留高階項,低階項直接丟棄 2 係數不要 3 執行次數是常數是為o 1...
時間複雜度 空間複雜度
演算法複雜度分為時間複雜度和空間複雜度。其作用 時間複雜度是指執行演算法所需要的計算工作量 而空間複雜度是指執行這個演算法所需要的記憶體空間。一 時間複雜度 時間頻度 乙個演算法執行所耗費的時間,從理論上是不能算出來的,必須上機執行測試才能知道。但我們不可能也沒有必要對每個演算法都上機測試,只需知道...