1. 程式人生 > >【簡單演算法】什麼是複雜度?

【簡單演算法】什麼是複雜度?

在上一篇文章裡,有看到一個簡單演算法題的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)`。 雖然測試工程師的程式碼對於複雜度要求不高甚至說非常低,但是我覺得理解複雜度,並且會做一些簡單的分析 還是很有必