算法時間複雜度分析

常用的算法的時間複雜度和空間複雜度

排序法

最差時間分析 平均時間複雜度 穩定度 空間複雜度
冒泡排序 O(n2) O(n2) 穩定 O(1)
快速排序 O(n2) O(n*log2n) 不穩定 O(log2n)~O(n)
選擇排序 O(n2) O(n2) 穩定 O(1)
二叉樹排序 O(n2) O(n*log2n) 不一頂 O(n)

插入排序

O(n2) O(n2) 穩定 O(1)
堆排序 O(n*log2n) O(n*log2n) 不穩定 O(1)
希爾排序 O O 不穩定 O(1)

1、時間複雜度
(1)時間頻度 一個算法執行所耗費的時間,從理論上是不能算出來的,必須上機運行測試才能知道。但我們不可能也沒有必要對每個算法都上機測試,只需知道哪個算法花費的時間多,哪個算法花費的時間少就可以了。並且一個算法花費的時間與算法中語句的執行次數成正比例,哪個算法中語句執行次數多,它花費時間就多。一個算法中的語句執行次數稱爲語句頻度或時間頻度。記爲T(n)。
(2)時間複雜度 在剛纔提到的時間頻度中,n稱爲問題的規模,當n不斷變化時,時間頻度T(n)也會不斷變化。但有時我們想知道它變化時呈現什麼規律。爲此,我們引入時間複雜度概念。 一般情況下,算法中基本操作重複執行的次數是問題規模n的某個函數,用T(n)表示,若有某個輔助函數f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值爲不等於零的常數,則稱f(n)是T(n)的同數量級函數。記作T(n)=O(f(n)),稱O(f(n)) 爲算法的漸進時間複雜度,簡稱時間複雜度。
在各種不同算法中,若算法中語句執行次數爲一個常數,則時間複雜度爲O(1),另外,在時間頻度不相同時,時間複雜度有可能相同,如T(n)=n2+3n+4與T(n)=4n2+2n+1它們的頻度不同,但時間複雜度相同,都爲O(n2)。 按數量級遞增排列,常見的時間複雜度有:常數階O(1),對數階O(log2n),線性階O(n), 線性對數階O(nlog2n),平方階O(n2),立方階O(n3),..., k次方階O(nk),指數階O(2n)。隨着問題規模n的不斷增大,上述時間複雜度不斷增大,算法的執行效率越低。 2、空間複雜度 與時間複雜度類似,空間複雜度是指算法在計算機內執行時所需存儲空間的度量。記作: S(n)=O(f(n)) 我們一般所討論的是除正常佔用內存開銷外的輔助存儲單元規模。討論方法與時間複雜度類似,不再贅述。
(3)漸進時間複雜度評價算法時間性能   主要用算法時間複雜度的數量級(即算法的漸近時間複雜度)評價一個算法的時間性能。
 

2、類似於時間複雜度的討論,一個算法的空間複雜度(Space Complexity)S(n)定義爲該算法所耗費的存儲空間,它也是問題規模n的函數。漸近空間複雜度也常常簡稱爲空間複雜度。
空間複雜度(Space Complexity)是對一個算法在運行過程中臨時佔用存儲空間大小的量度。一個算法在計算機存儲器上所佔用的存儲空間,包括存儲算法本身所佔用的存儲空間,算法的輸入輸出數據所佔用的存儲空間和算法在運行過程中臨時佔用的存儲空間這三個方面。算法的輸入輸出數據所佔用的存儲空間是由要解決的問題決定的,是通過參數表由調用函數傳遞而來的,它不隨本算法的不同而改變。存儲算法本身所佔用的存儲空間與算法書寫的長短成正比,要壓縮這方面的存儲空間,就必須編寫出較短的算法。算法在運行過程中臨時佔用的存儲空間隨算法的不同而異,有的算法只需要佔用少量的臨時工作單元,而且不隨問題規模的大小而改變,我們稱這種算法是“就地\"進行的,是節省存儲的算法,如這一節介紹過的幾個算法都是如此;有的算法需要佔用的臨時工作單元數與解決問題的規模n有關,它隨着n的增大而增大,當n較大時,將佔用較多的存儲單元,例如將在第九章介紹的快速排序和歸併排序算法就屬於這種情況。

當一個算法的空間複雜度爲一個常量,即不隨被處理數據量n的大小而改變時,可表示爲O(1);當一個算法的空間複雜度與以2爲底的n的對數成正比時,可表示爲0(10g2n);當一個算法的空I司複雜度與n成線性比例關係時,可表示爲0(n).若形參爲數組,則只需要爲它分配一個存儲由實參傳送來的一個地址指針的空間,即一個機器字長空間;若形參爲引用方式,則也只需要爲其分配存儲一個地址的空間,用它來存儲對應實參變量的地址,以便由系統自動引用實參變量。
 

算法時間複雜度分析示例
      爲了便於朋友們理解,我將不會採用教科書上慣用的快速排序、合併排序等經典示例進行分析,而是使用一個十分簡單的算法作爲示例。我們先來定義問題。
      問題定義:
      輸入——此問題輸入爲一個有序序列,其元素個數爲n,n爲大於零的整數。序列中的元素爲從1到n這n個整數,但其順序爲完全隨機。
      輸出——元素n所在的位置。(第一個元素位置爲1)

      這個問題非常簡單,下面直接給出其解決算法之一(僞代碼):

      LocationN(A)
      {
            for(int i=1;i<=n;i++)-----------------------t1
            {
                  if(A[i] == n) ----------------------------t2
                        { return i; }------------------------t3
            }
      }

      我們來看看這個算法。其中t1、t2和t3分別表示此行代碼執行一次需要的時間。
      首先,輸入規模n是影響算法執行時間的因素之一。在n固定的情況下,不同的輸入序列也會影響其執行時間。最好情況下,n就排在序列的第一個位置,那麼此時的運行時間爲“t1+t2+t3”。最壞情況下,n排在序列最後一位,則運行時間爲“n*t1+n*t2+t3=(t1+t2)*n+t3”。可以看到,最好情況下運行時間是一個常數,而最壞情況下運行時間是輸入規模的線性函數。那麼,平均情況如何呢?
      問題定義說輸入序列完全隨機,即n出現在1...n這n個位置上是等可能的,即概率均爲1/n。而平均情況下的執行次數即爲執行次數的數學期望,其解爲:

      E
      = p(n=1)*1+p(n=2)*2+...+p(n=n)*n
      = (1/n)*(1+2+...+n)
      = (1/n)*((n/2)*(1+n))
      = (n+1)/2

      即在平均情況下for循環要執行(n+1)/2次,則平均運行時間爲“(t1+t2)*(n+1)/2+t3”。
      由此我們得出分析結論:
      t1+t2+t3 <= F(n) <= (t1+t2)*n+t3,在平均情況下F(n) = (t1+t2)*(n+1)/2+t3

算法的漸近時間複雜度
      以上分析,我們對算法的時間複雜度F(n)進行了精確分析。但是,很多時候,我們不需要進行如此精確的分析,原因有下:
      1.在較複雜的算法中,進行精確分析是非常複雜的。
      2.實際上,大多數時候我們並不關心F(n)的精確度量,而只是關心其量級。
      基於此,提出漸近時間複雜度的概念。在正式給出漸近時間複雜度之前,要先給出幾個數學定義:

      定義一:Θ(g(n))={f(n) | 如果存在正常數c1、c2和正整數n0,使得當n>=n0時,0<c1g(n)<=f(n)<=c2g(n)恆成立}
      定義二:Ο(g(n))={f(n) | 如果存在正常數c和正整數n0,使得當n>=n0時,0<=f(n)<=cg(n)恆成立}
      定義三:Ω(g(n))={f(n) | 如果存在正常數c和正整數n0,使得當n>=n0時,0<=cg(n)<=f(n)恆成立}

      可以看到,三個定義其實都定義了一個函數集合,只不過集合中的函數需要滿足的條件不同。有了以上定義,就可以定義漸近時間複雜度了。
      不過這裏還有個問題:F(n)不是確定的,他是在一個範圍內變動的,那麼我們關心哪個F(n)呢?一般我們在分析算法時,使用最壞情況下的F(n)來評價算法效率,原因有如下兩點:
      1.如果知道了最壞情況,我們就可以保證算法在任何時候都不能比這個情況更壞了。
      2.很多時候,算法運行發生最壞情況的概率還是很大的,如查找問題中待查元素不存在的情況。且在很多時候,平均情況的漸近時間複雜度和最壞情況的漸近時間複雜度是一個量級的。

      於是給出如下定義:設F(n)爲算法A在最壞情況下F(n),則如果F(n)屬於Θ(g(n)),則說算法A的漸近時間複雜度爲g(n),且g(n)爲F(n)的漸近確界

      還是以上面的例子爲例,則在上面定義中F(n) = (t1+t2)*n+t3。則F(n)的漸近確界爲n,其證明如下:

      證明:
      設c1=t1+t2,c2=t1+t2+t3,n0=2
      又因爲 t1,t2,t3均大於0
      則,當n>n0時,0<c1n<=F(n)<=c2n 即 0<(t1+t2)*n<=(t1+t2)*n+t3<=(t1+t2+t3)*n恆成立。
      所以 F(n)屬於Θ(n)
      所以 n是F(n)的漸近確界
      證畢

      在實際應用中,我們一般都是使用漸近時間複雜度代替實際時間複雜度來進行算法效率分析。一般認爲,一個漸近複雜度爲n的算法要優於漸近複雜度爲n^2的算法。注意,這並不是說漸近複雜度爲n的算法在任何情況下都一定更高效,而是說在輸入規模足夠大後(大於臨界條件n0),則前一個算法的最壞情況總是好於後一個算法的最壞情況。事實證明,在實踐中這種分析是合理且有效的。
      類似的,還可以給出算法時間複雜度的上確界和下確界:
      設F(n)爲算法A在最壞情況下F(n),則如果F(n)屬於Ο(g(n)),則說算法A的漸近時間複雜度上限爲g(n),且g(n)爲F(n)的漸近上確界。
      設F(n)爲算法A在最壞情況下F(n),則如果F(n)屬於Ω(g(n)),則說算法A的漸近時間複雜度下限爲g(n),且g(n)爲F(n)的漸近下確界。

      這裏一定要注意,由於我們是以F(n)最壞情況分析的,所以,我們可以100%保證在輸入規模超過臨界條件n0時,算法的運行時間一定不會高於漸近上確界,但是並不能100%保證算法運行時間不會低於漸近下確界,而只能100%保證算法的最壞運行時間不會低於漸近下確界。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章