算法複雜度分析(下)

前一篇文章算法複雜度分析(上)講述了複雜度的大 O 表示法和幾個分析原則,這篇文章咱們來說講另外幾種複雜度,最好狀況時間複雜度(best case time complexity)、最壞狀況時間複雜度(worst case time complexity)、平均時間複雜度(average case time complexity)和均攤時間複雜度(amortized time complexity)。算法

最好、最壞狀況時間複雜度

顧名思義,這兩種時間複雜度指的是特殊狀況下的時間複雜度。咱們看下面的例子:數組

// n 表示數組 array 的長度
int find(int[] array, int n, int x) {
  int i = 0;
  int pos = -1;
  for (; i < n; ++i) {
    if (array[i] == x) {
        pos = i;
        break;
    }
  }
  return pos;
}
複製代碼

這段代碼實現的功能是,在數組 array 中尋找變量 x 第一次出現的位置,若沒有找到,則返回 -1;不然返回位置下標。bash

用上一篇文章的方法顯然是沒法分析這段代碼的複雜度的。由於,不一樣狀況下的時間複雜度是不一樣的。當數組中第一個元素就是要找的 x 時,時間複雜度是 O(1);而當最後一個元素纔是 x 時,時間複雜度則是 O(n)。微信

爲了表示代碼在不一樣狀況下的時間複雜度,就須要引入三個概念:最好狀況時間複雜度、最壞狀況時間複雜度和平均狀況時間複雜度。函數

其中,最好狀況時間複雜度就是在最理想狀況下執行代碼的時間複雜度,它的時間是最短的;最壞狀況時間複雜度就是在最糟糕狀況下執行代碼的時間複雜度,它的時間是最長的。post

平均狀況時間複雜度

最好、最壞時間複雜度反應的是極端條件下的複雜度,發生的機率不大,不能表明平均水平。那麼爲了更好的表示平均狀況下的算法複雜度,就須要引入平均時間複雜度。ui

繼續用前面 find 函數爲例,假設變量 x 在和不在數組 array 中的機率分別爲 1 / 2;當存在於數組中時,在每一個位置的機率均等,爲 1 / n。那麼,平均狀況時間複雜度就能夠用下面的方式計算:spa

((1 + 2 + ... + n) / n + n) / 2 = (3n + 1) / 43d

這個值就是機率論中的加權平均值,也叫指望值。因此平均狀況時間複雜度也叫加權平均時間複雜度或指望時間複雜度。可見,find 函數的平均時間複雜度爲 O(n)。code

大多數狀況下,不須要區分最好、最壞、平均狀況時間複雜度,只用一個複雜度就能夠知足需求了。只有當同一塊代碼在不一樣狀況下,時間複雜度有數量級上的區別時,才須要考慮這三種複雜度。

均攤時間複雜度

由上面咱們能夠知道,平均時間複雜度只有在某些特殊的時候纔會用到。均攤時間複雜度的應用場景比它更爲特殊。均攤時間複雜度是指,當大部分狀況下時間複雜度都很低,只有個別狀況下時間複雜度比較高時,而且這些操做之間存在着先後連貫的時序關係,這時候,能夠將較高時間複雜度的操做耗時均攤至時間複雜度較低的操做上。這種分析方法叫作攤還分析法,獲得的複雜度叫作均攤時間複雜度。

並且,在可以應用均攤時間複雜度分析的場合,通常均攤時間複雜度就等於最好狀況時間複雜度。

例如:

int[] array = new int(n);
int count = 0;

void addLast (int val) {
    if (count == array.length) {
        int[] newArray = new int(2 * n);
        for (int i = 0; i < 2 * n; i++) {
            newArray[i] = array[i];
        }
        newArray[count] = val;
        array = newArray;
    } else {
        array[count] = val
    }
    count++;
}
複製代碼

這段代碼實現的功能是往數組的末尾增長一個元素,若是數組沒有滿,直接日後面插入元素;若是數組滿了,即 count == array.length,則將數組擴容一倍,而後再插入元素。

例如,數組長度爲 n,則前 n 次調用 addLast() 複雜度都爲 O(1);第 n + 1 次則須要先進行 n 次元素轉移操做,而後再進行 1 次插入操做,複雜度爲 O(n)。並且很容易看出,O(1) 複雜度的操做和 O(n) 複雜度的操做出現頻率是有規律的,每 n 次 O(1) 操做後會跟隨一個 O(n) 操做。

那麼,就能夠將 O(n) 操做的複雜度均攤至每次 O(1) 操做中,均攤下來,這組操做的須要進行 (n + n * 1) / (n + 1) = 2n / (n + 1) 次操做,因此均攤複雜度爲 O(1)。


本文首發自微信公衆號《代碼寫完了》

相關文章
相關標籤/搜索