最好的那種狀況出現時,好比買彩票一次就中算法
最壞的狀況出現時,好比把這一期的彩票全買完才中數組
最好和最壞按照加權算起來,五五開!!數據結構
特殊的平均!!好比我好的少,壞的多,就把好的均攤到壞的上,獲得的就是壞的!!函數
// 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;
}
複製代碼
這個時候,問題就來了。這段代碼的時間複雜度是 O(n) 嗎?很顯然,我們上一節講的分析方法,解決不了這個問題。ui
由於,要查找的變量 x 可能出如今數組的任意位置。spa
最好時間複雜度 :若是數組中第一個元素正好是要查找的變量 x,那就不須要繼續遍歷剩下的 n-1 個數據了,那時間複雜度就是 O(1)。code
最壞時間複雜度:但若是數組中不存在變量x,那咱們就須要把整個數組都遍歷一遍,時間複雜度就成了 O(n)。cdn
平均時間複雜度 下面證blog
咱們知道,要查找的變量x,要麼在數組裏,要麼就不在數組裏。這兩種狀況對應的機率統計起來很麻煩,爲了方便你理解,咱們假設在數組中與不在數組中的機率都爲 1/2。另外,要查找的數據出如今 0~n-1 這 n 個位置的機率也是同樣的,爲 1/n。因此,根據機率乘法法則,要查找的數據出如今 0~n-1 中任意位置的機率就是 1/(2n)。it
這個值就是機率論中的加權平均值,也叫做指望值,因此平均時間複雜度的全稱應該叫加權平均時間複雜度或者指望時間複雜度。
咱們知道,時間複雜度的大O標記法中,能夠省略掉係數、低階、常量,
引入機率以後,前面那段代碼的加權平均值爲 (3n+1)/4。用大 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 值放到數組的第一個位置,而後再將新的數據插入。但若是數組一開始就有空閒空間,則直接將數據插入數組。
那這段代碼的時間複雜度是多少呢?你能夠先用咱們剛講到的三種時間複雜度的分析方法來分析一下。
最理想的狀況下,數組中有空閒空間,咱們只須要將數據插入到數組下標爲 count 的位置就能夠了, 因此最好狀況時間複雜度爲 O(1) 。最壞的狀況下,數組中沒有空閒空間了,咱們須要先作一次數組的遍歷求和,而後再將數據插入,因此最壞狀況時間複雜度爲 O(n)。
那平均時間複雜度是多少呢?答案是 O(1)。咱們仍是能夠經過前面講的機率論的方法來分析。
假設數組的長度是 n,根據數據插入的位置的不一樣,咱們能夠分爲 n 種狀況,每種狀況的時間複雜度是 O(1)。除此以外,還有一種「額外」的狀況,就是在數組沒有空閒空間時插入一個數據,這個時候的時間複雜度是 O(n)。並且,這 n+1 種狀況發生的機率同樣,都是 1/(n+1)。因此,根據加權平均的計算方法,咱們求得的平均時間複雜度就是:
至此爲止,前面的最好、最壞、平均時間複雜度的計算,理解起來應該都沒有問題。可是這個例子裏的平均複雜度分析其實並不須要這麼複雜,不須要引入機率論的知識。這是爲何呢?咱們先來對比一下這個 insert() 的例子和前面那個 find() 的例子,你就會發現這二者有很大差異。
首先 ,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)。這就是均攤分析的大體思路。
對一個數據結構進行一組連續操做中,大部分狀況下時間複雜度都很低,只有個別狀況下時間複雜度比較高,並且這些操做之間存在先後連貫的時序關係,這個時候,咱們就能夠將這一組操做放在一起分析,看是否能將較高時間複雜度那次操做的耗時,平攤到其餘那些時間複雜度比較低的操做上。並且,在可以應用均攤時間複雜度分析的場合,通常均攤時間複雜度就等於最好狀況時間複雜度。