一. 背景
在現實生活中,解決一個問題可以有多種方法,其中有好的方法,也有較為一般的方法。評判標准雖有不同,但總體思想是:用最小的代價獲得最多的收益。
這里所說代價並不僅指金錢開銷,有時也包括時間,所耗費資源等。
計算機程序也是為了解決問題而編寫的。同理可知,程序有好的,也有一般的,評判標准主要有兩方面:時間與空間。
人們都希望事情解決的越快越好,所以程序解決問題花費的時間一定要少。計算機資源是有限的,所以程序也要盡可能的少消耗資源。
不過很多時候不能兩全其美,此時應該抓主要矛盾。在編程時,很多場合會用空間消耗的增加來換取運行時間的減少。畢竟對於我們來說,時間一般會更加重要。
二. 引入
那么應該怎么比較不同算法之間的優劣呢?答:應該從時間與空間兩方面入手。
打個比方:
你的書架上有100本書,它們是任意放置的,沒有先后次序。你想在其中找到《算法導論》,那么你需要從頭找到尾,運氣好的話,第一本就是,運氣不好的話,你就要找到最后。
但是有一天你突發奇想,為每本書編號並且記住了,那么運用二分查找,最多只需要找7次。
忽然有一天,你買了1000本書,還是沒有先后次序地都放在書架上,並且又想找到《算法導論》,這次情況就會比較復雜,運氣好的話,第一本就是,運氣不好的話,你要找遍這1000本書。
但是你發現編號的方法比較好找,於是再次編號並運用二分查找,最多只要10次。
三. 大O記號
我們將按照書籍一一查找的方法,叫做方法一;將二分查找叫做方法二。
顯然,方法一的查找次數跟書籍的書量相關,你有1萬本書的時候,最壞情況就要找1萬次。而方法二則快的多。
方法一的算法叫做線性查找,當你有 n 本書的時候,最多需要 n 次查找。線性的意思是算法所需要的時間跟數據的量成正比。
方法二(二分查找)所需要的時間也跟數據量相關,但是並不會隨着數據量的增長而劇烈增加。
可以發現,算法的執行時間是與數據量有關的。
在方法一中,從 n 本書中找到一本書,最壞情況下需要找 n 次,我們將這種最壞情況下需要的時間記為 O( n )。最好情況只需要找一次,即第一本書就是,我們將這種情況記為 Ω (1)。
我們用大O表示最壞情況下需要的時間時間,用大 Ω 表示最好情況下需要的時間。不過最好情況並不總會發生,所以它的意義不大。
一般討論的都是最壞情況下需要的時間,它給了人們一種保證,即最壞情況下所用的時間是多少。
四. 時間還是次數?
1. 回到上面的例子中,你有 n 本書時,最壞情況需要的時間是 O( n ),那有 2n 本書時呢?答:也應該表示為 O ( n ),而不是 O ( 2n )。
我們關注的是算法所花時間隨着數據量增長的量級,應該拋去其中不重要的項,只留下起決定性因素的項,而不是具體的函數。
這里又會有一個新的問題。
假設一個成年人從100本書中找一本,最多需要100次,花費1小時;一個小孩從100本書中找一本,最多需要100次,花費10小時。
100次是相同的次數,而1小時和10小時卻不一樣。不同的人按照同一種工序執行一種操作,操作相同,但花費時間不同。
同樣一個程序,在不同的計算機上運行,最后花費的時間必然不一樣,由此可見,具體的時間並不能用來衡量算法的優劣。
我們發現,兩個人都用了100次,說明不管是誰來操作,都需要花費100次,這里是同一個算法在不同平台上的比較。
而不同算法之間的橫向比較,也需要通過次數來進行。
線性查找在A計算機上執行100次,花費1小時;在B計算機上執行100次,花費10小時。
二分查找在A計算機上執行7次,花費1分鍾;在B計算機上執行7次,花費10分鍾。
顯然,二分查找不論哪種情況,都要比線性查快,哪怕是兩台計算機的運行時間不一樣。
我們關注的就是這種增長率,而不是具體的時間。通過分析的手法,能夠提前估算算法需要的時間,而不是分別運行兩個算法再統計時間。
2. 在程序中,計算時間復雜度時,是按照語句的執行次數來計算的。
前面說過,同樣的程序,在不同計算機上的運行時間不同,不同的算法在不同的計算機上運行時間也不一樣,所以不應該用時間作為統一的衡量標准。
但是一個算法,在處理相同數據量時,語句運行的次數是一樣的,並且我們假設每條語句的運行時間相同(具體時間是多少無關緊要),為一個確定的數值,那么算法的時間復雜度用大O表示法來表示。