最好,最壞,平均,均攤複雜度分析

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

// 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;
}

要查找的變量 x 可能出現在數組的任意位置。如果數組中第一個元素正好是要查找的變量 x,那就不需要繼續遍歷剩下的 n-1 個數據了,那時間複雜度就是 O(1)。但如果數組中不存在變量 x,那我們就需要把整個數組都遍歷一遍,時間複雜度就成了 O(n)。所以,不同的情況下,這段代碼的時間複雜度是不一樣的。

爲了表示代碼在不同情況下的不同時間複雜度,我們需要引入三個概念:最好情況時間複雜度、最壞情況時間複雜度和平均情況時間複雜度。

最好情況時間複雜度就是,在最理想的情況下,執行這段代碼的時間複雜度。就像剛剛講到的,在最理想的情況下,要查找的變量 x 正好是數組的第一個元素,這個時候對應的時間複雜度就是最好情況時間複雜度。

同理,最壞情況時間複雜度就是,在最糟糕的情況下,執行這段代碼的時間複雜度。就像剛舉的那個例子,如果數組中沒有要查找的變量 x,我們需要把整個數組都遍歷一遍才行,所以這種最糟糕情況下對應的時間複雜度就是最壞情況時間複雜度。

平均情況時間複雜度

我們都知道,最好情況時間複雜度和最壞情況時間複雜度對應的都是極端情況下的代碼複雜度,發生的概率其實並不大。爲了更好地表示平均情況下的複雜度,我們需要引入另一個概念:平均情況時間複雜度,後面我簡稱爲平均時間複雜度。

依然是對上面的那段代碼,要查找的變量 x,要麼在數組裏,要麼就不在數組裏。這兩種情況對應的概率統計起來很麻煩,我們假設在數組中與不在數組中的概率都爲 1/2。另外,要查找的數據出現在 0~n-1 這 n 個位置的概率也是一樣的,爲 1/n。所以,根據概率乘法法則,要查找的數據出現在 0~n-1 中任意位置的概率就是 1/(2n)。接着第一個數執行1次,第二個數執行二次,以此類推得
在這裏插入圖片描述
接着再用大O表示法去掉常量與係數 得到的時間複雜度依舊爲O(n)

均攤時間複雜度
// array表示一個長度爲n的數組
// 代碼中的array.length就等於n
int[] array = new int[n];
int count = 0;
void insert(int val) {
 if (count == array.length) {
  int sum = 0;
  for (int i = 0; i < array.length; ++i) {
   sum = sum + array[i];
  }
  array[0] = sum;
  count = 1;
 }
 array[count] = val;
 ++count;
}

這段代碼實現了一個往數組中插入數據的功能。當數組滿了之後,也就是代碼中的 count == array.length 時,我們用 for 循環遍歷數組求和,並清空數組,將求和之後的 sum 值放到數組的第一個位置,然後再將新的數據插入。但如果數組一開始就有空閒空間,則直接將數據插入數組

比較這個插入操作與上面的查找操作
首先,find() 函數在極端情況下,複雜度才爲 O(1)。但 insert() 在大部分情況下,時間複雜度都爲 O(1)。只有個別情況下,複雜度才比較高,爲 O(n)。這是 insert()第一個區別於 find() 的地方。

我們再來看第二個不同的地方。對於 insert() 函數來說,O(1) 時間複雜度的插入和 O(n) 時間複雜度的插入,出現的頻率是非常有規律的,而且有一定的前後時序關係,一般都是一個 O(n) 插入之後,緊跟着 n-1 個 O(1) 的插入操作,循環往復

所以,針對這樣一種特殊場景的複雜度分析,我們並不需要像之前講平均複雜度分析方法那樣,找出所有的輸入情況及相應的發生概率,然後再計算加權平均值。

每一次 O(n) 的插入操作,都會跟着 n-1 次 O(1) 的插入操作,所以把耗時多的那次操作均攤到接下來的 n-1 次耗時少的操作上,均攤下來,這一組連續的操作的均攤時間複雜度就是 O(1)。這就是均攤分析的大致思路

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章