本篇文章收錄於專輯:http://dwz.win/HjKjava
前言
你好,我是彤哥,一個天天爬二十六層樓還不忘讀源碼的硬核男人。算法
上一節,咱們從過後統計法過渡到漸近分析法,詳細講解了如何進行算法的複雜度分析。數組
可是,若是遵循嚴格的漸近分析法,須要掌握大量數學知識,這無疑給咱們評估算法的優劣帶來了很大的挑戰。架構
那麼,有沒有更好地評估算法的方法呢?url
答案是必然的,本節,咱們就從最壞、平均、最好三種狀況來分析分析複雜度。.net
案例
爲了便於講解,我寫了一個小例子:code
public class LinearSearch { public static void main(String[] args) { int[] array = new int[]{1, 8, 9, 3, 5, 6, 10, 13}; int index = search(array, 10); System.out.println("index=" + index); } private static int search(int[] array, int value) { for (int i = 0; i < array.length; i++) { if (array[i] == value) { return i; } } return -1; } }
這個例子使用線性搜索從一個數組中查找一個元素,這個元素有可能存在,也有可能不存在於數組中。blog
最壞狀況
在最壞狀況下,要查找的元素不存在於數組中,此時,它的時間複雜度是多少呢?get
很簡單,必然須要遍歷完全部元素纔會發現要查找的元素不存在於數組中。源碼
因此,最壞狀況下,使用線性查找的時間複雜度爲O(n)。
平均狀況
在平均狀況下,咱們要照顧到每個元素,此時,它的時間複雜度如何計算呢?
在上一節,咱們已經講過計算方式了,不過,這裏考慮到有元素不存在於數組中,因此,是(n+1)種可能:
1*1/(n+1) + 2*/(n+1) + ... + n*1/(n+1) + (n+1)/(n+1) = 1/(n+1) * (n+1)(n+2)/2 = (n+2)/2
因此,在平均狀況下,忽略掉常數項,使用線性查找的時間複雜度也是O(n)。
爲何要忽略掉常數項?
當n趨向於無窮大的時候,常數項的意義不是很大,因此,能夠忽略,好比(n+2)/2=n/2 + 1,n自己已經趨向於無窮大,加不加1有什麼意義呢,n的倍數是2仍是1/2並不會有明顯的差異。
一樣地,低階項通常也會抹掉,好比2n^2 + 3n + 1,當n趨向於無窮大的時候,n^2的值是遠遠大於3n的,因此,不須要保留3n。
因此,計算複雜度時一般都會把常數項和低階項抹掉,只保留高階項。
最好狀況
最好狀況是什麼呢?
若是咱們要查找的元素正好是數組的第一個元素,查找一次就找到了,這無疑是最好的狀況。
因此,在最好狀況下,使用線性查找的時間複雜度是O(1)。
小結
經過上面的分析,能夠看到,最壞狀況和最好狀況是比較好評估的,而平均狀況則比較難以計算。
可是,最好狀況又不能表明大多數樣本,且平均狀況與最壞狀況在省略常數項的狀況下每每是比較接近的。
因此,一般,咱們使用最壞狀況來評估算法的時間複雜度,這也是比較簡單的一種評估方法,且每每也是比較準確的。
後記
本節,咱們從最壞、平均、最好三種狀況分析了線性查找的時間複雜度,通過詳細地分析,咱們得出結論,一般使用最壞狀況來評估算法的時間複雜度。
請注意,咱們這裏使用了「一般」,說明有些狀況是不能使用最壞狀況來評估算法的時間複雜度的。
那麼,你知道什麼狀況下不能使用最壞狀況來評估算法的時間複雜度嗎?
下一節,咱們接着聊。
關注公衆號「彤哥讀源碼」,解鎖更多源碼、基礎、架構知識!