今天有同事在檢查代碼的時候,因爲函數寫的性能不是很好,被打回去重構了,細思極恐,今天和你們分享一篇用js講解的時間複雜度和空間複雜度的博客算法
以前有看過的,你可能會看到這麼一串東西數組
T(n) = O(f(n)) S(n) = O(f(n))
這個叫作大O表示法,其中的T表明的是算法須要執行的總時間瀏覽器
S表示的算法須要的總空間性能優化
f(n)表示的是代碼執行的總次數函數
舉個例子性能
function go(n) { var item = 0; // 這裏執行了一次 for (var i = 0; i < n; i++) { //這裏執行了N次 for (var j = 0; j < n; j++) { //這裏執行了n*n次 item = item + i + j; //這裏執行了n*n次 } } return item; //這裏執行了一次 }
因此說上邊這段代碼是 1+n+n*n*2+1=2+n+2n²
優化
也就是說 T(n) = O(f(2+n+2n²)) spa
而後以前說了時間複雜度看的是一個代碼執行的時間的趨勢, 因此說在N,也就是規模比較大的時候,那些常量是起不到決定性的做用的,因此這個時候咱們忽略這些常量,這裏的例子是一個單段的代碼,這裏只看最大量級的循環就能夠了code
因此最後的這個代碼的時間複雜度是T(n) = O(n²) blog
你們能夠想一想一下數據中平方的曲線圖
首先什麼是時間複雜度,時間複雜度這個定義若是在以前沒有接觸過的話,你可能會認爲他表明的是一個代碼執行的時間,其實否則,算法的時間複雜度就是說一個算法的執行時間根據數據規模增加的一個趨勢,並非說代碼執行的具體時間
for (var i = 0; i < n; i++) {
sum += i;
}
通俗易懂,這段代碼的執行時間徹底由N來控制,因此說T(n) = O(n)
固然還有個更簡單的O(1)
function total(n) {
console.log(1)
}
不管怎麼樣,這段函數不受任何參數影響,代碼走一遍就完事,這種的代碼用T(n) = O(1) 表示
上邊的例子已經說了一個了兩層循環的那種,在舉一個時間複雜度多塊代碼的狀況時間複雜度的計算方式
function go(i) { var sum = 0; for (var j = 0; j < i; j++) { sum += i; } return sum; } function main(n) { var res = 0; for (var i = 0; i < n; i++) { res = res + go(i); // 這裏是重點 } }
在上邊的代碼種第二段代碼裏邊調用了第一段代碼,因此說在這個代碼裏邊是
go:(1+n)
在main函數裏邊的時候是(1+n*go)=(1+n+n*n)
因此最後的時間複雜度是T(n) = O(n²)
上邊距離說明的T(n) = O(n²) ,是一個函數在另外一個函數裏邊被調用,這種狀況是被把兩個函數的時間複雜度相乘。
還有另一種狀況,就是說在一個函數裏邊有多塊代碼,可是並無被相互調用,那麼這種狀況的時候,咱們只須要取複雜度最大的代碼塊就能夠了
好比說
function go(n) { for (var i = 0; i < n; i++) { for (var j = 0; j < n; j++) { console.log(1) } } for (var i = 0; i < n; i++) { console.log(2) } }
上邊這塊代碼中,第一塊代碼有兩層循環,經過上邊的例子咱們已經得知複雜度是
n²
下邊這塊代碼,是n
那麼在這種狀況的時候,當N接近無限大的時候N是對n²起不到決定性做用的,因此上邊這塊代碼的時間複雜度就是取最大值的n²
var i = 1; while (i <= n) { i = i * 10; }
在這段代碼中,能夠看到while裏邊,做爲判斷條件的i被每次*10,那麼因此說最後循環的次數並非n次,而是說十分之一n次,因此說這個時候的時間複雜度是10i=n,
i=logn
因此得出結論就是時間複雜度是T(n)=O(logn)
而後還有一種狀況就是經過改變的變量去增長循環次數的,同理是增長了時間複雜度
還有一些其餘的狀況好比時間複雜度相加
function go(m,n) { for (var i = 0; i < n; i++) { console.log(1) } for (var i = 0; i < m; i++) { console.log(2) } }
請看上邊這一段,這段代碼裏邊一個函數裏邊有兩個循環,可是形參有兩個,咱們如今沒法得知n和m到底誰大誰小,因此說這個時候代碼的時間複雜度是O(m+n)
上邊說了那麼一大堆的時間複雜度,相比各位已經比較瞭解了,就名字來看,時間複雜度看的是代碼的執行時間的趨勢,那麼同理的,空間複雜度就是指的佔用內存的趨勢
空間複雜度沒有時間複雜度那麼複雜,常見的就那麼幾種
畢竟我感受不會有人一直循環着各類花樣的聲明變量吧。。。
若是有,那麼請打死。。。。
let a = 1; let b = 1; let c = 1; let d = 1;
很簡單,O(1)
let arr =Array(n)
看這句代碼,代碼中建立了一個n長度的數組,很明顯數組的長度根據n來決定,因此說
O(n)
這裏須要說明一下,這裏沒有用循環,是由於只要不是在循環裏邊不停的聲明變量,只改變值的話是不會層架空間複雜度的
let arr=[] for (var i = 0; i < n; i++) { arr[i]=i for (var j = 0; j < n; j++) { arr[i][j]=j } }
怎麼樣,猛的一看這個代碼是否是很刺激,我以爲若是有這種狀況的話,通常都會被亂棍打死了。。。
再說優化以前我先盜一張圖給你們看一下各個複雜度的曲線圖,方便你們有一個直觀的認識
舉個比較簡單的優化的例子
console.time('a') function go(n) { var item = 0; for (var i = 1; i <= n; i++) { item += i; } return item; } console.timeEnd('a') console.time('b') function go2(n) { var item = n*(n+1)/2 return item; } console.timeEnd('b') go(1000) go2(1000)
你們能夠打印一下看一下
但願你們原諒我數學很差,記得以前看到過一個等差數列的例子,想不到其餘的性能優化的例子
但願你們看完以後能夠了解這些概念,有的時候這個東西真的很重要,找一個曲線比較高的例子
斐波那契,就是從第三項開始依次等於前兩項的和
function Fibonacci(n) { if (n <= 1 ) { return n; } else { return Fibonacci(n - 1) + Fibonacci(n - 2); } } console.time('b') Fibonacci(????) console.timeEnd('b')
有興趣的能夠試試打印一下,看看時間,不過大概50次的時候你得瀏覽器就應該沒有響應了,具體請往上看曲線圖。。。。
以上是我對時間複雜度和空間複雜度的一些認識,有不足或者不對的地方,但願指出來
o(* ̄▽ ̄*)ブ