資料結構與演算法筆記(二)複雜度分析
2. 複雜度分析
2.1 什麼是複雜度分析
資料結構和演算法的本質:快和省,如何讓程式碼執行得更快、更省儲存空間。
演算法複雜度分為時間複雜度和空間複雜度,從執行時間和佔用空間兩個維度來評估資料結構和演算法的效能。
複雜度描述的是演算法執行時間(或佔用空間)與資料規模的增長關係,越高階複雜度的演算法,執行效率越低。
2.2 為什麼需要複雜度分析
如果採用效能測試,測試結果非常依賴測試環境和受資料規模的影響很大。而複雜度分析有不依賴執行環境、成本低、效率高、易操作、指導性強的特點。
2.3 如何進行復雜度分析
2.3.1 大O表示法
不是程式碼真正執行的時間,而是表示程式碼執行時間隨資料規模增長的變化趨勢,叫作漸進時間複雜度,簡稱時間複雜度。
表示程式碼執行的時間; 表示資料規模的大小; 表示每行程式碼執行的次數總和; 表示程式碼的執行時間 與 表示式成正比。
2.3.2 時間複雜度
- 只關注迴圈執行次數最多的一段程式碼
- 加法規則:總複雜度等於量級最大的那段程式碼的複雜度
- 乘法規則:巢狀程式碼的複雜度等於巢狀內外程式碼複雜度的乘積
- 程式碼複雜度由兩個資料規模決定的,不能簡單利用加法規則,複雜度為
幾種常見時間複雜度:
-
多項式量級:
- 常量階 :程式碼執行時間不隨n的增大而增長
- 對數階
- 線性階
- 線性對數階
- 平方階 、立方階 、 k次方階
-
非多項式量級:當資料規模越來越大時,演算法執行時間會急劇增加,非常低效
- 指數階
- 階乘階
2.3.3 空間複雜度
漸進空間複雜度,表示演算法的儲存空間與資料規則之間的增長關係。
常見的空間複雜度:
2.4 時間複雜度情況
-
最好情況時間複雜度(best case time complexity)
-
最壞情況時間複雜度(worse case time complexity)
-
平均情況時間複雜度(average case time complexity)
-
均攤時間複雜度(amortized time complexity)
2.4.1 最好情況時間複雜度
在最理想的情況下,執行這段程式碼的時間複雜度。
以下面這段程式碼為例,
// n 表示陣列 array 的長度
int find(int[] array, int n, int x) {
int i = 0;
int pos = -1;
for (; i < n; ++i) {
if (array[i] == x) {
pos = i;
break;
}
}
return pos;
}
如果要查詢的變數x正好是陣列的第一個元素,對應的時間複雜度即為最好情況時間複雜度。
2.4.2 最壞情況時間複雜度
在最糟糕的情況下,執行這段程式碼的時間複雜度。
以剛剛舉的例子,如果陣列中沒有要查詢的變數x,需要把整個陣列都遍歷一遍時,此時對應的時間複雜度即為最壞情況時間複雜度。
2.4.3 平均情況時間複雜度
也叫加權平均時間複雜度或期望時間複雜度,用程式碼在所有情況下執行的次數的加權平均值表示。
假設在陣列中與不在陣列中的概率都為 ,且要查詢的資料出現在 0~n-1 這 n 個位置的概率也是一樣的,都為 。因此,根據概率乘法法則,要查詢的資料出現在 0~n-1 中任意位置的概率是 。那麼平均情況複雜度的計算過程為:
2.4.4 均攤時間複雜度
對一個數據結構進行一組連續操作中,大部分情況下時間複雜度都很低,只有個別情況下時間複雜度比較高,而且這些操作出現的頻率是非常有規律的,之間存在前後連貫的時序關係。此時,就可以將這一組操作一起分析,將較高時間複雜度的那次操作的耗時,平攤到其他那些時間複雜度比較低的操作上。能夠應用均攤時間複雜度分析的場合,一般均攤時間複雜度等於最好情況時間複雜度。
均攤時間複雜度應用的場景特殊、有限,可以看成一種特殊的平均時間複雜度。
2.4.5 總結
只有同一塊程式碼在不同的情況下,時間複雜度有量級的差距,我們才會使用這幾種複雜度表示法來區別。一般情況下,使用一個複雜度就可以滿足需求。