天天看點

怎樣計算一個算法的複雜度?

作者:黑馬程式員

分析一個算法主要看這個算法的執行需要多少機器資源。在各種機器資源中,時間和空間是兩個最主要的方面。是以,在進行算法分析時,人們最關心的就是運作算法所要花費的時間和算法中使用的各種資料所占用的空間資源。算法所花費的時間通常稱為時間複雜度,使用的空間資源稱為空間複雜度。接下來學習如何計算一個算法的時間複雜度和空間複雜度。

1.時間複雜度

在進行算法分析時,語句總的執行次數T(n)是關于問題規模n的函數,然後分析T(n)随n的變化。

怎樣計算一個算法的複雜度?

這樣用大寫的O來标記算法的時間複雜度,稱之為大O(Order的簡寫)标記法。一般随着n的增長,T(n)也會随之增長,其中T(n

)增長最慢者就是時間性能最優的算法。

在計算時間複雜度的時候,根據T(n)與n的最高階數關系,我們給這些算法的複雜度進行了歸類,如表1所示。

怎樣計算一個算法的複雜度?

當然還會有一些其他階數關系,這裡隻是列出了幾種較常見的關系。算法的執行次數可能會與規模n呈現出這些關系,那麼這些關系又是如何推導出來的呢?下面給出大O階的推導方法:

(1)用常數1取代運作中的所有加法常數。

(2)在修改後的運作次數函數中,隻保留最高階項。

(3)如果最高階項存在,且不是1,則除去其常系數,得到的結果就是大O階。

接下來通過分析幾段程式的執行過程來推導出其時間複雜度,程式段1代碼如下所示:

int a=100;              //執行一次

int b=200;              //執行一次

int sum=a+b;            //執行一次

printf("&d\n",sum);      //執行一次           

上述程式段有4行代碼,每一行執行1次,加起來一共執行了4次,f(n)=4,即T(n)=O(4)。根據推導方法中的第一條,将常數項以1代替。在保留其最高階項時,發現其沒有最高階項,是以該算法的時間複雜度為O(1),為常數階。程式段2代碼如下所示:

void func()
{
    int i,sum=0;                         //執行一次
    for (i=0;i<=100;i++)
    {
    sum +=i;                              //執行n次
    }

    printf("sd\n",sum);               //執行一次
}           

該程式段的執行次數為1+n+1,則f(n)=n+2,即T(n)=O(n+2)。然後将常數項以1替換,且隻保留最高階項,則得出T(n)=O(n),是以該算法的時間複雜度為O(n),為線性階。

程式段3代碼如下所示:

void func()
{
    int i=l;
    do
    {
    i*=2;
    }
    while (i<n);
}           

在這個程式段中,當i<n時,循環結束。如果循環了f(n)次,則2(fn)=n,即f(n)=log2n,T(n)=O(log2n)。然後消除常系數,保留最高階項,最後得出T(n)=O(logn),為對數階。

用大O階來推導算法的複雜度并不難,讀者在以後的學習中設計算法,就可以用此法來估測算法的優劣。

2.空間複雜度

空間複雜度是對一個算法在運作過程中所占存儲空間大小的度量,一般也作為問題規模n的函數,以數量級形式給出,格式如下所示:

怎樣計算一個算法的複雜度?

一個算法的存儲量包括輸入資料所占空間、程式本身所占空間和輔助變量所占空間。在對算法進行分析時,隻考慮輔助變量所占空間。若所需輔助空間相對于輸入資料量來說是常數,則稱此算法為原地工作。若所用空間量依賴于特定的輸入,則除了有特殊說明外,均按最壞情況考慮。

有時候,在寫代碼時可以用空間來換取時間,例如,寫一個算法來判斷某年是否是閏年,這樣每輸入一個年份都要調用算法去判斷一下,在時間上就有點複雜。為了提高效率,可以用空間來換取時間,即建立一個大小合适的資料,編号從0到n,如果是閏年,則存入資料1,否則存入資料0。這樣隻要通過判斷年份編号上存儲的是0還是1就知道該年份是否是閏年了。

用空間換取時間可以将運算最小化,但這兩種情況哪種更好,要結合具體情況而定。一般情況下,都是用時間複雜度來度量算法,當不加限定地使用“複雜度”這一術語時,都是指時間複雜度。