本文將詳細介紹算法複雜度算法
大O表示法是描述算法的性能和複雜程度。 分析算法時,時常遇到如下幾類函數數組
符號 名稱 O(1) 常數的 O(log(n)) 對數的 O((log(n))c) 對數多項式的 O(n) 線性的 O(n2) 二次的 O(nc) 多項式的 O(cn) 指數的
如何衡量算法的效率?一般是用資源,例如CPU(時間)佔用、內存佔用、硬盤佔用和網絡佔用。當討論大O表示法時,通常考慮的是CPU(時間)佔用網絡
下面用一些例子來理解大O表示法的規則數據結構
【O(1)】機器學習
function increment(num){ return ++num; }
假設運行increment(1)函數,執行時間等於X。若是再用不一樣的參數(例如2)運行一次increment函數,執行時間依然是X。和參數無關,increment函數的性能都同樣。所以,咱們說上述函數的複雜度是O(1)(常數)函數
【O(n)】性能
如今以順序搜索算法爲例:學習
function sequentialSearch(array, item){ for (var i=0; i<array.length; i++){ if (item === array[i]){ //{1} return i; } } return -1; }
若是將含10個元素的數組([1, ..., 10])傳遞給該函數,假如搜索1這個元素,那麼,第一次判斷時就能找到想要搜索的元素。在這裏咱們假設每執行一次行{1} ,開銷是1。spa
如今,假如要搜索元素11。行{1}會執行10次(遍歷數組中全部的值,而且找不到要搜索的元素,於是結果返回 -1)。若是行{1}的開銷是1,那麼它執行10次的開銷就是10,10倍於第一種假設code
如今,假如該數組有1000個元素([1, ..., 1000])。搜索1001的結果是行{1}執行了1000次(而後返回-1)
sequentialSearch函數執行的總開銷取決於數組元素的個數(數組大小),並且也和搜索的值有關。若是是查找數組中存在的值,行{1}會執行幾回呢?若是查找的是數組中不存在的值,那麼行{1}就會執行和數組大小同樣屢次,這就是一般所說的最壞狀況
最壞狀況下,若是數組大小是10,開銷就是10;若是數組大小是1000,開銷就是1000。能夠得出sequentialSearch函數的時間複雜度是O(n),n是(輸入)數組的大小
回到以前的例子,修改一下算法的實現,使之計算開銷:
function sequentialSearch(array, item){ var cost = 0; for (var i=0; i<array.length; i++){ cost++; if (item === array[i]){ //{1} return i; } } console.log('cost for sequentialSearch with input size ' + array.length + ' is ' + cost); return -1; }
用不一樣大小的輸入數組執行以上算法,能夠看到不一樣的輸出
【O(n2)】
用冒泡排序作O(n2)的例子:
function swap(array, index1, index2){ var aux = array[index1]; array[index1] = array[index2]; array[index2] = aux; } function bubbleSort(array){ var length = array.length; for (var i=0; i<length; i++){ //{1} for (var j=0; j<length-1; j++ ){ //{2} if (array[j] > array[j+1]){ swap(array, j, j+1); } } } }
假設行{1}和行{2}的開銷分別是1。修改算法的實現使之計算開銷:
function bubbleSort(array){ var length = array.length; var cost = 0; for (var i=0; i<length; i++){ //{1} cost++; for (var j=0; j<length-1; j++ ){ //{2} cost++; if (array[j] > array[j+1]){ swap(array, j, j+1); } } } console.log('cost for bubbleSort with input size ' + length + ' is ' + cost); }
若是用大小爲10的數組執行bubbleSort,開銷是100(102)。若是用大小爲100的數組執 行bubbleSort,開銷就是10 000(1002)。須要注意,咱們每次增長輸入的大小,執行都會愈來愈久
時間複雜度O(n)的代碼只有一層循環,而O(n2)的代碼有雙層嵌套循環。如 果算法有三層遍歷數組的嵌套循環,它的時間複雜度極可能就是O(n3)
下圖比較了前述各個大O符號表示的時間複雜度:
下表是經常使用數據結構的時間複雜度
下表是圖的時間複雜度:
下表是排序算法的時間複雜度:
下表是搜索算法的時間複雜度:
通常來講,若是一個算法的複雜度爲O(nk),其中k是常數,咱們就認爲這個算法是高效的,這就是多項式算法
對於給定的問題,若是存在多項式算法,則計爲P(polynomial,多項式)
還有一類NP(nondeterministicpolynomial,非肯定性多項式)算法。若是一個問題能夠在多項式時間內驗證解是否正確,則計爲NP。若是一個問題存在多項式算法,天然能夠在多項式時間內驗證其解。所以,全部的P都是NP。然而,P=NP是否成立,仍然不得而知。NP問題中最難的是NP徹底問題,它知足如下兩個條件:(1)是NP問題,也就是說,能夠在多項式時間內驗證解,但尚未找到多項式算法;(2)全部的NP問題都能在多項式時間內歸約爲它。爲了理解問題的歸約,考慮兩個決策問題L和M。假設算法A能夠解決問題L,算法B能夠驗證輸入y是否爲M的解。目標是找到一個把L轉化爲M的方法,使得算法B能夠用於構造算法A
還有一類問題,只需知足NP徹底問題的第二個條件,稱爲NP困難問題。所以,NP徹底問題也是NP困難問題的子集
下面是知足P < > NP時,P、NP、NP徹底和NP困難問題的歐拉圖:
非NP徹底的NP困難問題的例子有停機問題和布爾可知足性問題(SAT)。 NP徹底問題的例子有子集和問題、旅行商問題、頂點覆蓋問題等等
咱們提到的有些問題是不可解的。然而,仍然有辦法在符合要求的時間內找到一個近似解。啓發式算法就是其中之一。啓發式算法獲得的未必是最優解,但足夠解決問題了。啓發式算法的例子有局部搜索、遺傳算法、啓發式導航、機器學習等