一.爲何要學習算法?算法
先來個簡單的算法比較:求sum=1+2+3+...+(n-1)+n的結果. 輸入整數n,輸出 sum
解法一:for循環數組
function sum(n){ var s=0; //執行1次 for(var i=1;i<n+1;i++){ s+=i; //執行n+1次
}
return s; //執行1次
}
解法二:函數
function sum(n){ return n*(n-1)/2; //執行1次 }
很明顯,解法二要優於解法一。由於解法二須要運算的次數少。咱們去衡量一個算法的好壞主要是從時間複雜度和空間複雜度來看的,其次纔到可讀性,可維護性。那麼接下來說講怎麼來計算時間複雜度與空間複雜度。學習
二.時間複雜度的計算:spa
推導大O階來計時間複雜度code
規則:1.用常數1取代運行時間中的全部加法常數(即常數階都計爲O(1) );blog
2.在修改後的運行次數函數中,只保留最高階項;io
3.若是最高階項存在且不是1,則去除與這個項相乘的常數for循環
解法一的運行次數: f1(n) = 1+n+1+1=n+3 次 時間複雜度記做 T(n) = O( f1(n) ) //n+3直接舍掉常數,變爲n ,名爲「線性階」function
解法二的運行次數: f2(n) = 1次 時間複雜度記做 T(n) = O( f1(1) ) //名爲「常數階」
由此可知解法一隨n的增長,運行次數也增長,而解法二始終只需運行一次
對數階:
function count(n){ var c=1; //執行1次 while(c<n){ c=c*2; //執行log2n次
} return c; }
也就是說2的多少次冪大於n,就運行了多少次。時間複雜度計作O(logn),名爲對數階。
平方階:
function num(n){ var count=0; for(var i=0;i<n;i++){ //執行 n 次 for(var j=i;j<n;j++){ count++; //執行 n-i 次 } } return count; }
以上代碼執行總次數爲n+(n-1)+(n-2)+...+1 = n2/2+n/2 次,用大O推導法去掉相加常數n/2,去掉相乘常數1/2,因此時間複雜度爲O(n2)
總結:
時間複雜度有多種,這裏是討論常見的階。經常使用的時間複雜雜耗時的時間從小到大依次爲:
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) <O(nn)
擴展:最壞時間複雜度
例:給出一個數組arr,裏面有n個隨機數,找出arr中的指定數字。那麼這個數字有可能出如今數組中的第一個位置,時間複雜度爲O(1);也可能出如今數組最後一個位置,時間複雜度爲O(n) ,從機率來講,平均查找時間應該是n/2次。
最壞時間複雜度從字面上就能理解,時間最長的狀況,時間不會更長,狀況不會更壞了。一般沒有特殊說明,咱們計算的時間複雜度都爲最壞時間複雜度。
三.算法空間複雜度
算法的空間複雜度並非計算實際佔用的空間,而是計算整個算法的輔助空間單元的個數,與問題的規模沒有關係。算法的空間複雜度S(n)定義爲該算法所耗費空間的數量級。S(n)=O(f(n)) 若算法執行時所須要的輔助空間相對於輸入數據量n而言是一個常數,則稱這個算法的輔助空間爲O(1)。一般,咱們用時間複雜度來衡量算法的優略。