1、時間複雜度分析有哪些?
- 最好情況時間複雜度(best case time complexity)
- 最壞情況時間複雜度(worst case time complexity)
- 平均情況時間複雜度(average case time complexity)
- 均攤時間複雜度(amortized time complexity)
2、最好、最壞情況時間複雜度。
最好情況時間複雜度就是在最理想的情況下,執行這段代碼的時間複雜度。
最壞情況時間複雜度就是在最糟糕的情況下,執行這段代碼的時間複雜度。
1 // n表示數組array的長度 2 int find(int[] array, int n, int x) { 3 int i = 0; 4 int pos = -1; 5 for (; i < n; ++i) { 6 if (array[i] == x) { 7 pos = i; 8 break; 9 } 10 } 11 return pos; 12 }
上面這段代碼要實現的功能是:在一個數組中查找變量 x 出現的位置,如果找到了,就馬上跳出循環,返回它的位置值;如果找不到,就返回 -1。
這裏不能只是看到了 for 循環就判定其時間複雜度爲 O(n),因爲這個數組的順序是不確定的,有可能數組中的第一個元素就是 x,那就可以馬上結束循環了,其時間複雜度就是 O(1);如果數組中不存在變量 x 或者是數組中的最後一個元素纔是 x,那就需要遍歷整個數組,時間複雜度就是 O(n)。
在這裏,O(1) 就是最好情況時間複雜度,O(n) 就是最壞情況時間複雜度。
3、平均情況時間複雜度
最好、最壞情況時間複雜度都是極端情況下的代碼複雜度,發生的概率很小。因此,我們還需要知道平均情況時間複雜度。
還是以剛剛查找變量 x 的位置的例子爲例,要查找的變量 x 在數組中的位置,總共有 n+1 種情況:在數組的 0~n-1 位置中和不在數組中。我們把每種情況下,查找需要遍歷的元素個數累加起來再除以 n+1,就可以得到需要遍歷的元素個數的平均值,即:
省略掉係數、低階、常量,將以上公式進行簡化之後,得到的平均時間複雜度就是 O(n)。
但是,以上的 n+1 種情況,沒有將各種情況發生的概率考慮進去。這裏可以引入概率論的相關知識,假設變量 x 在數組中與不在數組中的概率各爲 1/2,出現在 0~n-1 這 n 個位置的概率都是 1/n。根據概率乘法法則,要查找的數據出現正在 0~n-1 中任意位置的概率就是 1/(2n)。
這樣,就可以得到以下計算過程:
這個值就是概率論中的加權平均值,也叫做期望值。根據這個加權平均值,去掉係數和常量,我們得到的平均時間複雜度也是 O(n)。所以,平均時間複雜度就是:加權平均時間複雜度(亦稱爲期望時間複雜度)。
大部分情況下,我們並不需要區分最好、最壞、平均三種複雜度,平均複雜度只在某些特殊情況下纔用到。
4、均攤時間複雜度
均攤時間複雜度:對一個數據結構進行一組連續操作中,大部分情況下時間複雜度都很低,只有個別情況下時間複雜度較高。而且這些操作之間存在前後連貫的時序關係,在這個時候,我們可以將這一組操作放在一塊兒分析,看是否能將較高時間複雜度那次操作的耗時,平攤到其他那些時間複雜度較低的操作上。
均攤時間複雜度的應用場景比平均時間複雜度更加特殊、更加有限。
1 // array表示一個長度爲n的數組 2 // 代碼中的array.length就等於n 3 int[] array = new int[n]; 4 int count = 0; 5 6 void insert(int val) { 7 if (count == array.length) { 8 int sum = 0; 9 for (int i = 0; i < array.length; ++i) { 10 sum = sum + array[i]; 11 } 12 array[0] = sum; 13 count = 1; 14 } 15 16 array[count] = val; 17 ++count; 18 }
這段代碼實現的是往一個數組中插入數據的功能,當數組滿了以後,也就是 count == array.length 的時候,我們用 for 循環遍歷數組求和,再將新的數據插入,其時間複雜度爲 O(n)。但如果數組未滿,則直接將數據插入數組,其時間複雜度爲 O(1)。
個人體會: 平均和均攤基本算是一個概念,均攤是特殊的平均。出現O(1)的次數遠大於出現O(n)出現的次數,那麼平均和均攤時間複雜度就是O(1)。
我們來分析一下它的時間複雜度,數組的長度爲 n,根據數據插入的不同位置,可以分爲 n 種情況,每種情況的時間複雜度爲 O(1)。還有一種最“糟糕”的情況,那就是數組已滿,這個時候的時間複雜度爲 O(n)。而且,這 n+1 種情況發生的概率是一樣的,都是 1/(n+1)。所以,根據加權平均的計算方法,可知:
對比一下 insert() 的例子和前面 find() 的例子,這兩個例子的最好、最壞情況時間複雜度都一樣,爲什麼平均時間複雜度相差這麼多呢?
這兩個例子之間最大的區別在於:find() 的最好、最壞情況時間複雜度都是在極端情況下才會發生,而 insert() 在大部分情況下,時間複雜度都是 O(1),只有在極端情況下,時間複雜度纔是 O(n)。其次,對於 insert() 函數來說,每當碰到一個時間複雜度爲 O(n) 的情況,接下來就會有 n-1 個 O(1) 的插入操作,循環往復。
均攤下來,這一組連續的操作的均攤時間複雜度就是 O(1)。這就是均攤時間複雜度分析的大致思路。