資料結構與演算法——時間複雜度

Jaeshn發表於2020-09-30

複雜度用於衡量程式的執行效率,複雜度是一個關於資料量n的函式,一般用大O表示法來表示執行的量級,常見的時間複雜度有O(1)、O(logn)、O(n)、O(n2)、O(n3)、O(2n)、O(n!)。

大O表示法表示的複雜度與常係數無關,比如計算後複雜度為O(n)和O(2n)的程式碼,他們的時間複雜度都是O(n);如果計算後的時間複雜度是多項式級的時間複雜度,那麼程式碼的時間複雜度則以最高次冪為準,比如計算後複雜度為O(n2+n)與O(n2)的兩段程式碼,他們的時間複雜度都是O(n2);對於O(1)的時間複雜度,O(1)指的是程式碼通過使用有限的可數的資源就能完成,與輸入的資料量n無關。

如何計算時間複雜度?

《演算法之美》一書在介紹大O表示法的時候介紹過,大O符號是用來測量演算法最壞情況的速記法。大O符號的目的不是使用分鐘和秒鐘來表示演算法的效能,而是方便我們討論問題的規模和程式的執行時間之間的關係。

大O表示法的關於n的函式的計算為程式碼執行次數的總和,最終的時間複雜度由最大量級的項確定;

對於分多段的程式碼,總的程式碼時間複雜度就等於量級最大的那段程式碼的時間複雜度;對於巢狀程式碼,時間複雜度等於巢狀的多段程式碼的時間複雜度的乘積。

對於一個程式中出現的兩個不同規模的程式碼段,如果不能確定大小,那麼在累加的時候不能取其中一個作為時間複雜度,比如兩段程式碼的時間複雜度分別為O(m)和O(n),且無法比較m和n的大小,那麼程式的時間複雜度應為O(n+m);對於兩個無法衡量量級大小的巢狀程式碼段的複雜度,跟累加的相同,該程式的時間複雜度也應該是O(m*n)。

最好情況時間複雜度

程式在最好的情況下執行的時間複雜度,比如插入一個元素到陣列,如果剛好要插入到陣列末尾,那麼時間複雜度為O(1),這是陣列插入元素的最好情況時間複雜度。

最壞情況時間複雜度

程式在最糟糕的情況下執行的時間複雜度,就上面的陣列插入元素,假如我們要插入到陣列的第一個位置,由於陣列是一片連續的記憶體空間,在儲存資料時是按順序儲存的,當我們要把資料插入到陣列的頭部,那麼我們要把陣列的所有元素都按順序一個一個往後移一位,此時需要遍歷整個陣列,時間複雜度為O(n),這就是插入元素的最壞情況下的時間複雜度。

平均情況時間複雜度

平均情況時間複雜度,主要是針對像上面的陣列插入元素,在已經有n個元素的陣列中插入一個元素,有n種情況需要進行資料的移動,即當我們要把資料插入第0到第n-1個位置時,我們都需要進行資料的移動,所以往陣列中插入元素,一共有n+1種情況,最後一種情況是直接將資料插入到陣列的末尾,這裡的時間複雜度可以根據每種情況發生的概率來計算期望時間複雜度。
由於在每個位置插入資料的概率是一樣的,所以在陣列中插入資料的時間複雜度應該是(0+1+2+…+n)/(n+1)=(n(n+1))/2(n+1)=n/2,時間複雜度為O(n)。分子為在每個位置插入資料時需要移動的資料的和。

均攤時間複雜度
均攤時間複雜度跟平均情況時間複雜度不是一個概念,均攤時間複雜度主要是將耗時多的操作均攤到耗時少的操作上。比如在陣列尾部插入元素,這裡是只要陣列中還有空間,那麼就往陣列後面插入資料,當陣列滿了之後,要再插入資料,需要申請一個更大的空間然後將陣列原來的內容搬到新的空間裡,再繼續往陣列後面插入元素,在這種場景中,每進行一次O(n)複雜度的操作,前面都有n-1次O(1)時間複雜度的操作,整個過程中只有一步是耗時多的,那麼可以把耗時多的這一步的操作的複雜度均攤到前面n-1次操作上,即(1+1+1+…+1+n)/n=(2n-1)/n。得到的均攤時間複雜度為O(1),也就是在陣列尾部插入元素的時間複雜度為O(1),均攤時間複雜度只有在類似的應用場景中計算比較方便,其他時候分析時間複雜度還是以前面的分析方法為準。

相關文章