前言
針對某一類問題的解決,我們可能需要藉助演算法來實現,實現的手段也可能是各式各樣的。雖然最終都解決了問題,但是各個解決手段,也就是演算法還是存在優劣之分的。
既然存在比較,那肯定就有一個標準供來參考,那麼我們在評價一個演算法的優劣時參考的標準是什麼呢?
演算法的優劣主要從它執行時所佔用的「時間」和「空間」兩個方面來進行評定,也就是我們常聽到的「時間複雜度」和「空間複雜度」。
- 時間複雜度:執行演算法所需要的計算工作量,可以估算出程式對處理器的使用程度。
- 空間複雜度:執行當前演算法所需要的記憶體空間,可以估算出程式對處理器的使用程度。
時間複雜度
談到是時間複雜度,我們很多人的第一反應就是將演算法執行一遍,列印出其執行的時間就是它所消耗的時間,其實這樣是不可行的,因為:
- 解決一個問題的演算法可能有很多種,一一實現的工作量無疑是巨大的,得不償失;
- 不同計算機的軟、硬體環境不同,即便使用同一臺計算機,不同時間段其系統環境也不相同,程式的執行時間很可能會受影響,嚴重時甚至會導致誤判。
實際場景中,我們更喜歡用一個估值來表示演算法所程式設計序的執行時間。所謂估值,即估計的、並不準確的值。注意,雖然估值無法準確的表示演算法所程式設計序的執行時間,但它的得來並非憑空揣測,需要經過縝密的計算後才能得出。
表示一個演算法所程式設計序執行時間的多少,用的並不是準確值(事實上也無法得出),而是根據合理方法得到的預估值。
我們一般用“大 O 符號表示法”來表示時間複雜度:T(n) = O(f(n))
- n 是影響複雜度變化的因子
- f(n) 是複雜度具體的演算法
- O 表示正比例關係
這個公式的全稱是:演算法的漸進時間複雜度。
大 O 符號表示法並不是用於來真實代表演算法的執行時間的,它是用來表示程式碼執行時間的增長變化趨勢的。
我們來看一個常見的例子:
for(let index = 0; index < n; index++){
console.log(index);
}
可以看到,這段程式中僅有 2 行程式碼,其中:
- for 迴圈從 index 的值為 0 一直逐增至 n(注意,迴圈退出的時候 index 值為 n),因此 for 迴圈語句執行了 n+1 次;
- 而迴圈內部僅有一條語句,index 的值每增 1 該語句就執行一次,一直到 index 的值為 n-1,因此,列印語句一共執行了 n 次。
因此,整段程式碼中所有語句共執行了 (n+1)+n 次,即 2n+1 次。資料結構中,每條語句的執行次數,又被稱為該語句的頻度。整段程式碼的總執行次數,即整段程式碼的頻度。
常見的時間複雜度量級
- 常數階O(1)
- 對數階O(logN)
- 線性階O(n)
- 平方階O(n^2)
- 立方階O(n^3)
- K次方階O(n^k)
- 指數階(2^n)
這裡僅介紹了以最壞情況下的頻度作為時間複雜度,而在某些實際場景中,還可以用最好情況下的頻度和最壞情況下的頻度的平均值來作為演算法的時間複雜度。
空間複雜度
和時間複雜度類似,一個演算法的空間複雜度,也常用大 O 記法表示。空間複雜度比較常用的有:
- O(1)
- O(n)
- O(n²)
要知道每一個演算法所編寫的程式,執行過程中都需要佔用大小不等的儲存空間,例如:
程式程式碼本身所佔用的儲存空間;
程式中如果需要輸入輸出資料,也會佔用一定的儲存空間;
程式在執行過程中,可能還需要臨時申請更多的儲存空間。
首先,程式自身所佔用的儲存空間取決於其包含的程式碼量,如果要壓縮這部分儲存空間,就要求我們在實現功能的同時,儘可能編寫足夠短的程式碼。
程式執行過程中輸入輸出的資料,往往由要解決的問題而定,即便所用演算法不同,程式輸入輸出所佔用的儲存空間也是相近的。
事實上,對演算法的空間複雜度影響最大的,往往是程式執行過程中所申請的臨時儲存空間。不同的演算法所編寫出的程式,其執行時申請的臨時儲存空間通常會有較大不同。
如果程式所佔用的儲存空間和輸入值無關,則該程式的空間複雜度就為 O(1);反之,如果有關,則需要進一步判斷它們之間的關係:
- 如果隨著輸入值 n 的增大,程式申請的臨時空間成線性增長,則程式的空間複雜度用 O(n) 表示;
- 如果隨著輸入值 n 的增大,程式申請的臨時空間成 n2 關係增長,則程式的空間複雜度用 O(n2) 表示;
- 如果隨著輸入值 n 的增大,程式申請的臨時空間成 n3 關係增長,則程式的空間複雜度用 O(n3) 表示;
比如:
let m = 0;
for(let index = 0; index < 9999; index++){
m++;
}
雖然 m 的值隨著 index 的增加在一直變化,可是並未產生新的變數,即程式所佔用的空間並未發生變化,所以,它的空間複雜度為 O(1)。
總結
時間複雜度和空間複雜度都是一種經過嚴謹推算得出的預估值,並不能代表實際情況。
時間複雜度和空間複雜度代表的是一種趨勢。
我們一般情況下所說的時間複雜度和空間複雜度,都是最壞情況下的執行趨勢,實際情況可能比預估的要好。
多數業務場景下,一個好的演算法往往更注重的是時間複雜度的比較,而空間複雜度只要在一個合理的範圍內就可以。
~
~ 本文完,感謝閱讀!
~
學習有趣的知識,結識有趣的朋友,塑造有趣的靈魂!
大家好,我是〖程式設計三昧〗的作者 隱逸王,我的公眾號是『程式設計三昧』,歡迎關注,希望大家多多指教!
你來,懷揣期望,我有墨香相迎! 你歸,無論得失,唯以餘韻相贈!
知識與技能並重,內力和外功兼修,理論和實踐兩手都要抓、兩手都要硬!
本作品採用《CC 協議》,轉載必須註明作者和本文連結