數據結構與算法學習-複雜度分析

前言

這一篇筆記主要記錄總結了什麼是算法複雜度?、爲什要作算法複雜度分析?、如何作算法複雜度分析?、經常使用的複雜度級別?以及如何掌握複雜度分析?等問題。算法

算法複雜度分析是什麼?

數據結構與算法解決的是如何更省、更快的存儲和處理數據的問題。所以就須要一個考量效率和資源消耗的方法,這就是複雜度分析方法。數組

複雜度也叫漸進複雜度,其中包含時間複雜度空間複雜度兩個維度,是用來分析算法執行時間(或者佔用空間)數據規模的增加關係,能夠粗略的表示,越高階複雜度的算法,執行效率越低。數據結構

爲何要作算法複雜度分析?

  1. 統計、監控等過後統計法的侷限性:性能

    1. 測試結果高度依賴測試環境
    2. 測試結果受數據規模影響很大
  2. 所以須要一個不用具體的測試數據就能夠粗略地估計算法的執行效率的方法。複雜度分析不依賴執行環境、成本低、可操做性強。學習

  3. 掌握複雜度分析,將能寫出性能更優的代碼。測試

如何作算法複雜度分析?

大 O 複雜度表示法

  • 全部代碼的執行時間 T(n) 與每行代碼的執行次數 n 成正比。
  • 大 O 時間複雜度實際上並不具體表明代碼真正的執行時間,而是表示代碼執行時間隨數據規模增加變化趨勢,叫作漸進時間複雜度,簡稱時間複雜度
  • 因爲公式中的低階、常量、係數對增加趨勢不影響,因此均可以忽略,只須要記錄一個最大的量級。

算法複雜度分析法則

  1. 只關注循環執行次數最多的一段代碼
  2. 加法法則:總複雜度等於量級最大的那段代碼的複雜度
  3. 乘法法則:嵌套代碼的複雜度等於嵌套內外代碼複雜度的乘積

空間複雜度分析

類比時間複雜度,空間複雜度全稱就是漸進空間複雜度,表示算法的存儲空間數據規模之間增加關係ui

常見的空間複雜度是:O(1)、O(n)、O(n^2)spa

最好、最壞時間複雜度

對有些代碼,其複雜度有不是固定的,有最好、最壞的狀況。如數組元素查找,有可能第一次就找到,這個時候算法時間複雜度是常數階O(1),也有可能須要整個遍歷一遍數組才能找到,這個時候是最壞的時間複雜度O(n)。code

  • 最好時間複雜度:在最理想狀況下,代碼執行的時間複雜度。
  • 最壞時間複雜度:在最糟糕的狀況下,代碼執行的時間複雜度。

平均狀況複雜度

因爲最好和最壞狀況複雜度對應的都是極端狀況下的代碼複雜度,發生的機率並不大,因此須要引入平均狀況複雜度來更好的表示算法的時間複雜度。cdn

平均狀況複雜度 是將各類狀況下時間複雜度發生的機率考慮進去,而後計算總體時間複雜度的指望值,因此平均狀況複雜度也叫作加權平均時間複雜度或者指望時間複雜度

不少時候,只要使用一個複雜度就能夠知足需求了。只有同一塊代碼在不一樣狀況下,時間複雜度有量級的差距,纔會使用這三種複雜度表示法來區分。

均攤時間複雜度

**適用場景:**對一個數據結構進行一組連續操做中,大部分狀況下時間複雜度都很低,只有個別狀況下時間複雜度比較高,並且這些操做之間存在先後連貫的時序關係,這個狀況下,就能夠將這一組操做放在一塊兒分析,看可否將較高時間複雜度的那次操做平攤到其餘那些時間複雜度較低的操做上。通常在可以應用均攤時間複雜度的場景,均攤時間複雜度就等於最好狀況時間複雜度

分析方法:攤還分析

經常使用的算法複雜度級別

  • 非多項式量級

    • O(2^n) 和 O(n!)
    • 當數據規模 n 越大時,非多項式量級的算法的執行時間會急劇增長。因此非多項式量級算法是很是低效的算法,所以要避免使用到。
  • 多項式量級

    • O(1):代碼的執行時間不隨 n 的增大而增加,這樣代碼的時間複雜度記作 O(1),通常狀況下,若是算法中不存在循環語句、遞歸語句、無論有多少行代碼,其時間複雜度都是 O(1)。
    • O(logn)、O(nlogn):對數階複雜度。
    • O(m+n)、O(m*n):代碼的複雜度由兩個數據的規模來決定。
  • 多項式量級算法時間複雜度隨數據規模的增加變化趨勢曲線

如何掌握複雜度分析

多練習。

課後問題

1. 項目以前都會進行性能測試,再作代碼的時間複雜度、空間複雜度分析是否是屢次一舉?

**解答:**不是畫蛇添足,漸進時間,空間複雜度分析爲咱們提供了一個很好的理論分析的方向,成本低,可操做性強,不用具體的測試數據就能夠粗略地估計算法的執行效率,有助於寫出性能高效的代碼,同時培養起算法複雜度分析的思惟,能夠和性能測試相輔相成。

2. 分析如下代碼的複雜度

// 全局變量,大小爲 10 的數組 array,長度 len,下標 i。
int array[] = new int[10]; 
int len = 10;
int i = 0;

// 往數組中添加一個元素
void add(int element) {
   if (i >= len) { // 數組空間不夠了
     // 從新申請一個 2 倍大小的數組空間
     int new_array[] = new int[len*2];
     // 把原來 array 數組中的數據依次 copy 到 new_array
     for (int j = 0; j < len; ++j) {
       new_array[j] = array[j];
     }
     // new_array 複製給 array,array 如今大小就是 2 倍 len 了
     array = new_array;
     len = 2 * len;
   }
   // 將 element 放到下標爲 i 的位置,下標 i 加一
   array[i] = element;
   ++i;
}
複製代碼

解答:

  • 最好時間複雜度:當 i 小於 len 時,代碼不執行 if 語句,算法時間複雜度是 O(1),
  • 最壞時間複雜度:當 i >= len 時,代碼開始執行 if 語句,裏面有個循環操做,因此算法時間複雜度是 O(n)。
  • 平均狀況複雜度:進入 if 語句後,數組擴容 2 倍,數組每次插入元素的機率都是 1/len + 1,因此根據加權時間複雜度計算公式爲 1 * 1/len + 1 + 1 * 1/len + 1 + ... + len *(1/(len+1)) = 1,因此平均狀況複雜度是 O(1)。
  • 均攤時間複雜度:每次 O(len)前都有 len 次 O(1),存在先後連貫的時序關係,因此能夠將高時間複雜度的 O(len) 平攤到以前的 len 次上,因此均攤時間複雜度是 O(1)。

分享我的技術學習記錄和跑步馬拉松訓練比賽、讀書筆記等內容,感興趣的朋友能夠關注個人公衆號「青爭哥哥」。

相關文章
相關標籤/搜索