【javascript系列】時間複雜度和空間複雜度

1、前言

時間複雜度和空間複雜度,咱們在大學裏的算法與數據結構課程中已經學習過,這回根據項目工做中整理一下,這個估計只是一個粗略的估計分析,並非一個準確的估計分析。算法

一、學習時間複雜度和空間複雜度是頗有必要的,這個屬於算法與數據結構的範疇,學這個是爲了解決代碼中的「快」和「省」的問題。這樣才能使你的代碼運行效率更高,佔用空間更小。代碼執行效率須要經過複雜度分析。數組

二、數據規模的大小會影響到複雜度分析。好比排序,若是是一個有序的數組,執行效率會更高;若是數據量不多的時候,這個算法看不出性能上差異。數據結構

三、好比說不一樣物理機環境不同,好比i3,i5,i7的cpu等等,運行內存1G,2G,4G,8G等等;時間上確定有差異。函數

2、大O的複雜度

咱們來看個簡單的例子,一個循環累加器。性能

function total(n) { 
      var sum = 0;   //t
      for (var i = 0; i < n; i++) {   // nt
        sum += i;   //nt
      } 
      return sum;  // t
    }

分析:假設每一行代碼執行耗時都同樣,爲t,這樣整個代碼總執行時間爲(t+nt+nt+t)=(2n+2)t。學習

咱們再來看一個栗子:優化

function total(n) { 
      var sum = 0;      // t
      for (var i = 0; i < n; i++) {   //nt
        for (var j = 0; j < n; j++) {     //n*n*t
          sum = sum + i + j;     //n*n*t
        }
      }
      return sum;      //t
    }

分析:假設每一行代碼執行耗時都同樣,爲t,這樣整個代碼總執行時間爲(t+nt+nnt2+t)=(2nn+n+2)t。spa

從數學角度來看,咱們能夠得出個規律:代碼的總執行時間 T(n) 與每行代碼的執行次數成正比.code

因此上邊兩個函數的執行時間能夠標記爲 T(n) = O(2n+2) 和 T(n) = O(2n*n+n+2)。這就是大 O 時間複雜度表示法,它不表明代碼真正的執行時間,而是表示代碼隨數據規模增加的變化趨勢,簡稱時間複雜度。blog

並且當 n 很大時,咱們能夠忽略常數項,只保留一個最大量級便可。因此上邊的代碼執行時間能夠簡單標記爲 T(n) = O(n) 和 T(n) = O(n2)。

3、時間複雜度分析

一、循環次數最多的代碼塊

在分析的時候,只須要關心哪一個代碼塊循環次數最多的那段代碼,好比說剛纔的第一個例子,循環最多的代碼塊是這兩行,循環都是n次。

for (var i = 0; i < n; i++){
    sum += i;
}

執行了n次,因此事件複雜度就是O(n)。

二、最大值原則:總複雜度等於最大的代碼塊的複雜度

function total(n) { 
      // 第一個 for 循環
      var sum1 = 0; 
      for (var i = 0; i < n; i++) {
        for (var j = 0; j < n; j++) {
          sum1 = sum1 + i + j; 
        }
      }
      // 第二個 for 循環
      var sum2 = 0;
      for(var i=0;i<1000;i++) {
        sum2 = sum2 + i;
      }
      // 第三個 for 循環
      var sum3 = 0;
      for (var i = 0; i < n; i++) {
        sum3 = sum3 + i;
      }
      return {sum1:sum1, sum2: sum2, sum3:sum3}
    }

分別分析每一段循環時間複雜度,取他們最大的量級決定整段代碼複雜度。

第一段,時間複雜度 O(n2)。

第二段,時間複雜度能夠忽略,循環執行了 1000 次,是個常數量級,儘管對代碼的執行時間會有影響,可是當 n 無限大的時候,就能夠忽略。

第三段,時間複雜度O(n)。

綜上所述,因此上述代碼的時間複雜度爲 O(n2)。

三、乘法原則:嵌套代碼複雜度等於嵌套內外代碼複雜度乘積

舉個例子:

function f(i) {
      var sum = 0;
      for (var j = 0; j < i; j++) {
        sum += i;
      }
      return sum;
    }
    function total(n) {
      var res = 0;
      for (var i = 0; i < n; i++) {
        res = res + f(i); // 調用 f 函數
      }
    }

分析一下:total方法時間複雜度O(n),f方法的時間複雜度O(n)。

因此整段代碼的時間複雜度O(n2)。

4、常見的時間複雜度分析

最高量級的複雜度,效率是遞減的

image

如上圖能夠粗略的分爲兩類,多項式量級和非多項式量級。其中,非多項式量級只有兩個:O(2n) 和 O(n!) 對應的增加率以下圖所示

image

當數據規模 n 增加時,非多項式量級的執行時間就會急劇增長,因此,非多項式量級的代碼算法是很是低效的算法。

一、常數階複雜度O(1)

O(1) 只是常量級時間複雜度表示法,並非代碼只有一行,舉個例子:

function total() {
      var sum = 0;
      for(var i=0;i<100;i++) {
        sum += i;
      }
      return sum;
    }

雖然有這麼多行,即便 for 循環執行了 100 次,可是代碼的執行時間不隨 n 的增大而增加,因此這樣的代碼複雜度就爲 O(1)。

二、對數階O(logn)和O(nlogn)

(1)對數階時間複雜度的常見代碼以下:

function total1(n) {
      var sum = 0;
      var i = 1;
      while (i <= n) {
        sum += i;
        i = i * 2;
      }
    }
    function total2(n) {
      var sum = 0;
      for (var i = 1; i <= n; i = i * 2) {
        sum += i;
      }
    }

上面函數total1和total2有一個相同點:變量 i 從 1 開始取值,每循環一次乘以 2,當大於 n 時,循環結束。

因此真正循環了x次。2x =n;,因此 x = log2n。

因此上面兩個函數時間複雜度都是 O(log2n)。

(2)咱們在舉個例子:

function total1(n) {
      var sum = 0;
      var i = 1;
      while (i <= n) {
        sum += i;
        i = i * 3;
      }
    }
    function total2(n) {
      var sum = 0;
      for (var i = 1; i <= n; i = i * 3) {
        sum += i;
      }
    }

同理可知:這兩個函數的時間複雜度爲 O(log3n) 。

因爲咱們能夠忽略常數,也能夠忽略對數中的底數,因此在對數階複雜度中,統一表示爲 O(logn);那 O(nlogn) 的含義就很明確了,時間複雜度 爲O(logn) 的代碼執行了 n 次。

三、其餘複雜度O(m+n)和O(m*n)

舉個例子:

function total(m,n) {
      var sum1 = 0;
      for (var i = 0; i < n; i++) {
        sum1 += i;
      }
      var sum2 = 0;
      for (var i = 0; i < m; i++) {
        sum2 += i;
      }
      return sum1 + sum2;
    }

由於咱們沒法評估 m 和 n 誰的量級比較大,因此就不能忽略掉其中一個,這個函數的複雜度是有兩個數據的量級來決定的,因此此函數的時間複雜度爲 O(m+n);那麼 O(m*n) 的時間複雜度相似。

5、空間複雜度分析

空間複雜度的話和時間複雜度相似推算。 所謂空間複雜度就是表示算法的存儲空間和數據規模之間的關係。

舉個例子:

function initArr(n) {
      var arr = [];
      for (var i = 0; i < n; i++) {
        arr[i] = i;
      }
    }

時間複雜度的推算,忽略掉常數量級,每次數組賦值都會申請一個空間存儲變量,因此此函數的空間複雜度爲 O(n)。

常見的空間複雜度只有 O(1)、O(n)、O(n2)。其餘的話不多會用到。

6、複雜度優化

function total(n) {
      var sum = 0;
      for (var i = 1; i <= n; i++) {
        sum += i;
      }
      return sum;
    }

這段代碼咱們很容易知道時間複雜度 O(n)。

可是我想把複雜度降一降,下降到常數階O(1)。

其實求和怎麼求呢?等比數列,直接用公式,這就說明了數學好的人,算法應該高level點。

function total(n) {
      var sum = n*(n+1)/2
      return sum;
    }

上面函數的時間複雜度僅僅爲 O(1),在數據規模比較龐大的時候,下面的函數是否是明顯比上面的函數運算效率更高呢。

7、總結

分析算法執行效率與數據規模之間的增加關係,能夠粗略的表示,越高階複雜度的算法,執行效率越低。

複雜度學習以後,有時候能夠避免寫出效率低的代碼。

相關文章
相關標籤/搜索