關於時間複雜度空間複雜度的理解
對於非科班出身的人來說,在學習演算法的時候經常會遇到的問題就是關於對事件空間複雜度的 理解。
「大 O 表示法」的準確的數學描述方式非常枯燥,我在這裡就不貼出來湊字數了,其實大 O 表示法的意思挺簡單的,就是表示:隨著輸入的值變化,程式執行所需要的時間與輸入值的變化關係。
我們先看第一個程式碼,這是一個函式,輸入一個數組,輸出這個數組裡元數的和。
int count(int a[], int n) { int result = 0; for (int i = 0; i < n; ++i) { result += a[i]; } return result; }
這裡的時間複雜度是O(n),即隨著n的增長,O(n)也隨之線性增長,我們用O(n)來表示這種線性時間複雜度
接下來我們來看第二行程式碼
int binary_search(int A[], int key, int imin, int imax) { if (imax < imin) { return KEY_NOT_FOUND; } else { int imid = midpoint(imin, imax); if (A[imid] > key) return binary_search(A, key, imin, imid - 1); else if (A[imid] < key) return binary_search(A, key, imid + 1, imax); else return imid; } }
對於這個程式來說,如果它處理 N 個元素求和所花的時間是 T,那麼它處理 N 2 個元素的和所花的時間是多少呢?是 T 2 嗎?
如果頭腦算不清楚,我們可以拿實際的數字來實驗,二分查詢每次(幾乎)可以去掉一半的候選數字。所以假如 N = 1024,那麼它最多要找多少次呢?答案是 10 次,因為 2^10 = 1024,每次去掉一半,10 次之後就只剩下唯一一個元素了。
好,這個時候,如果元素的個數翻一倍,變成 2048 個,那麼它最多要找多少次呢?相信大家都能算出來吧?答案是 11 次,因為 2 ^ 11 = 2048。
所以在這個例子中,輸入的元素個數雖然翻倍,但是程式執行所花的時間卻只增加了 1,我們把這種時間複雜度要叫「對數」時間複雜度,用 O(logN) 來表示。
除了剛剛講的「線性」時間複雜度和「對數」時間複雜度。我們還有以下這次常見的時間復度數。
「常數」時間複雜度,例如返回一個有序陣列中的最小數,這個數因為始終在第一個位置,所以就不會受到陣列大小的影響,無論陣列多大,我們都可以在一個固定的時間返回結果。
「線性對數」時間複雜度,即 O(N*logN),這個複雜度比較常見,因為常見的高效的排序演算法,都是這個時間複雜度,比如快速排序,堆排序,歸併排序等。
之前我很多次在網上看到說把1000萬次運算當做實際的1s來運算。我的計算機主頻是2.5GHZ,所以每秒可以執行25億次彙編指令,加入每次迴圈裡面的程式碼可以產生250條彙編指令,才能夠得到1s1千萬次迴圈的結果,不是很確定,有機會可以yongxcode試一下
總結一下學習時間複雜度的知識對於我們的工作有什麼用:
-
對於不同的資料規模,能夠決策採用不同的解決方案。
-
瞭解什麼情況下用暴力解法就能夠解決問題,避免寫複雜的程式碼。
-
在寫程式碼之前,就能夠預估程式的執行時間,從而可以知道是否能夠滿足產品需求。
-
在程式出現效能瓶頸時,能夠有解決方案而不是抓瞎。
參考 https://www.cnblogs.com/mafeng/p/6831731.html