【簡單演算法】什麼是複雜度?
- 2020 年 12 月 22 日
- 筆記
- 一個小小測試的成長日常, 演算法練習
在上一篇文章里,有看到一個簡單演算法題的2個解法,我們運用了複雜度分析來判斷哪個解法更合適。
這裡的複雜度,就是用于衡量程式的運行效率的重要度量因素。
雖然有句俗話「不管是白貓還是黑貓,抓到老鼠就是好貓」,這句話是站在結果導向的,沒錯。但是如果
有個程式要去處理海量數據,一個程式設計師寫的要執行2天,而另一個程式設計師只要半小時,那麼第二種顯然更適合
我們的實際需求。
一、什麼是複雜度
複雜度是一個關於輸入數據量n的函數。
要表示複雜度很簡單,用大寫O加上括弧O()
將複雜度包起來就好了。比如這個程式碼的複雜度是f(n),那就可以寫成
O(f(n))
。
在計算複雜度的時候,有三點需要我們記住:
- 複雜度與具體常係數無關
- 多項式級複雜度相加,選擇高者為結果
O(1)
表示特殊複雜度
1、複雜度與具體常係數無關
舉個例子,將一個列表反轉,不用reverse()。
def demo_1():
a = [1, 2, 3, 4, 5]
b = [0 for x in range(0,5)] #第一個for循環
n = len(a)
for i in range(n): # 第二個for循環
b[n - i - 1] = a[i]
print(b)
if __name__ == "__main__":
demo_1()
===============運行結果==================
D:\Programs\Python\Python36\python.exe D:/練習/leecode/fuzadu.py
[5, 4, 3, 2, 1]
Process finished with exit code 0
可以看到我先用了一個for循環創建了一個跟a列表等長度,元素全是0的列表。
然後再用一個for循環將a里的元素倒序放入b,最終得到一個跟a反序的列表。
其中,每一個for循環的時間複雜度都是O(n)
,2個加起來就是O(n)+O(n)
,也等於O(n+n)
,也等於O(2n)
。
也就是相當於 一段 O(n)
複雜度的程式碼先後執行兩遍,它們的複雜度是一致的。
2、多項式級複雜度相加,選擇高者為結果
有了上面的例子,這個也就好理解了。
假設,一個演算法的複雜度是O(n²)+O(n)
,那麼可以知道,當n越來越大,也就是輸入的數據量越來越大時,n^2的變化率要比n大的多,
所以,這時候我們只取變化率更大的n^2來表示複雜度即可,也就是O(n²)+O(n)
等同於O(n²)
。
3、O(1)表示特殊複雜度
還是藉助上面的反轉問題,這裡再使用第二種解法。
def demo_2():
a = [1, 2, 3, 4, 5]
tmp = 0
n= len(a)
for i in range(n//2): # // 表示整數除法,返回不大於結果的一個最大整數
tmp = a[i]
a[i] = a[n -i -1]
a[n -i -1] = tmp
print(a)
if __name__ == "__main__":
demo_2()
==============運行結果==============
D:\Programs\Python\Python36\python.exe D:/練習/leecode/fuzadu.py
[5, 4, 3, 2, 1]
Process finished with exit code 0
跟第一個解法相比,第二個解法少了一個for循環,而且循環次數只是到了列表的一半,那麼時間複雜度就是O(n/2)
,
由於複雜度與具體的常係數無關的性質,這段程式碼的時間複雜度還是 O(n)
。
但是在空間複雜度上,第二個解法開闢了一個新的變數tmp
,它與數組長度無關。
輸入是 5 個元素的數組,需要一個tmp
變數輸入是 50 個元素的數組,同樣只需要一個tmp
變數。
因此,空間複雜度與輸入數組長度無關,這就是 O(1)
。
二、分析複雜度
這裡就直接上一些經驗性的結論,可以直接拿過來用的:
- 一個順序結構的程式碼,時間複雜度是
O(1)
。 - 一個簡單的 for 循環,時間複雜度是
O(n)
。 - 兩個順序執行的 for 循環,時間複雜度是
O(n)+O(n)=O(2n)
,其實也是O(n)
。 - 兩個嵌套的 for 循環,時間複雜度是
O(n²)
。 - 二分查找,時間複雜度都是
O(logn)
。
趁熱打鐵,分析一下下面程式碼的複雜度:
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
for (k = 0; k < n; k++) {
}
for (m = 0; m < n; m++) {
}
}
}
可以先從最裡面看,最內層是2個順序結構的for循環,複雜度是O(n)
。
中間這層的又嵌套了一個for循環,所以這時候複雜度就變成了O(n^2)
。
最後,最外層又嵌套了一個for循環,所以最終的複雜度就是O(n^3)
。
雖然測試工程師的程式碼對於複雜度要求不高甚至說非常低,但是我覺得理解複雜度,並且會做一些簡單的分析
還是很有必要的。