一個算法中的語句執行次數稱為語句頻度或時間頻度,記為T(n)。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(f(n)) 有可能相同,如T(n)=n^2+3n+4與T(n)=4n^2+2n+1它們的頻度不同,但時間復雜度相同,都為O(n^2)。
現在我們根據一些書本上和網絡上對時間復雜度概念的描述進行一下總結:
T(n),語句頻度,時間頻度,亦稱為時間復雜度。
O(f(n)),漸進時間復雜度。
前者T(n)是某個算法的時間耗費,它是該算法所求解 問題規模 n的函數,而后者O(f(n))是指當問題規模趨向無窮大時,該算法時間復雜度的數量級。當我們評價一個算法的時間性能時,主要標准就是算法的漸近時間復雜度O(f(n)),因此,在算法分析時,往往對兩者不予區分,經常是將漸近時間復雜度T(n)=O(f(n))簡稱為時間復雜度,其中的f(n)一般是算法中頻度最大的語句頻度。
注意:算法中語句的頻度不僅與問題規模有關,還與輸入實例中各元素的取值相關。但是我們總是考慮在最壞的情況下的時間復雜度。以保證算法的運行時間不會比它更長。
常見的時間復雜度,按數量級遞增排列依次為:常數階O(1)、對數階O(log2n)或O(lbn)、線性階O(n)、線性對數階O(n*log2n)、平方階O(n^2)、立方階O(n^3)、k次方階O(n^k)、指數階O(2^n)。
· 下面有一道題目是可以幫助同學們理解概念的:
1、設三個函數f,g,h分別為 f(n)=100*n^3+n^2+1000 , g(n)=25*n^3+5000*n^2 , h(n)=n^1.5+5000*n*lgn
請判斷下列關系是否成立:
(1) f(n)=O(g(n))
(2) g(n)=O(f(n))
(3) h(n)=O(n^1.5)
(4) h(n)=O(nlgn)
這里我們復習一下漸近時間復雜度的表示法T(n)=O(f(n)),這里的"O"是數學符號,它的嚴格定義是 "若T(n)和f(n)是定義在正整數集合上的兩個函數,則T(n)=O(f(n))表示存在正常數C和n0 ,使得當n≥n0時都滿足0≤T(n)≤C*f(n)。"(即書本中的定義)。通俗一點就是這兩個函數當整型自變量n趨向於無窮大時,兩者的比值是一個不等於0的常數。
◆ (1)成立。題中由於兩個函數的最高次項都是n^3,因此當n→∞時,兩個函數的比值是一個常數,所以這個關系式是成立的。
◆ (2)成立。與上同理。
◆ (3)成立。與上同理。
◆ (4)不成立。由於當n→∞時n^1.5比n*lgn遞增的快,所以h(n)與nlgn的比值不是常數,故不成立。
理解完概念之后,就開始求算法的時間復雜度。
從概念中我們知道,要求時間復雜度O(f(n)),就必須要知道算法中頻度最大的語句頻度f(n),那么要求最大的語句頻度f(n)就必須要知道算法的語句頻度T(n)。一般總的思路就是:T(n)->f(n)->O(f(n))。有時候可以直接找到算法中頻度最大的語句,直接算出f(n),然后寫出O(f(n))。也有例外情況就是很難求出語句頻度T(n)的
下面會用一些例子做詳細的說明:O(1)
例1:Temp=i;i=j;j=temp;
以上三條單個語句的頻度均為1,該程序段的執行時間是一個與問題規模n無關的常數。算法的時間復雜度為常數階,記作T(n)=O(1)。如果算法的執行時 間不隨着問題規模n的增加而增長,即使算法中有上千條語句,其執行時間也不過是一個較大的常數。此類算法的時間復雜度是O(1)。
例2:x=91; y=100;
while(y>0)
if(x>100)
{x=x-10;y--;}
else x++;
解答: T(n)=O(1), 這個程序看起來有點嚇人,總共循環運行了1000次,但是我們看到n沒有? 沒。這段程序的運行是和n無關的,就算它再循環一萬年,我們也不管他,只是一個常數階的函數。
O(n)
例1:
i=1; k=0
while(i<n)
{ k=k+10*i;i++;
}
解答:T(n)=n-1, T(n)=O(n), 這個函數是按線性階遞增的。
例2:
a=0;
b=1; ①
for (i=1;i<=n;i++) ②
{
s=a+b; ③
b=a; ④
a=s; ⑤
}
解: 語句1的頻度:2,
語句2的頻度: n,
語句3的頻度: n-1,
語句4的頻度:n-1,
語句5的頻度:n-1,
T(n)=2+n+3(n-1)=4n-1=O(n)
O(n^2)
例1: 交換i和j的內容
sum=0; (一次)
for(i=1;i<=n;i++) (n次 )
for(j=1;j<=n;j++) (n^2次 )
sum++; (n^2次 ) (此語句是算法中頻度最大的,可直接算出f(n)=n^2,得時間復雜度T(n)=O(n^2))
解:T(n)=2n^2+n+1 =O(n^2)
例2:
for (i=1;i<n;i++)
{
y=y+1; ①
for (j=0;j<=(2*n);j++)
x++; ②
}
解: 語句1的頻度是n-1
語句2的頻度是(n-1)*(2n+1)=2n^2-n-1
f(n)=2n^2-n-1+(n-1)=2n^2-2
該程序的時間復雜度T(n)=O(n^2).
O(n^3)
例1:
for(i=0;i<n;i++)
{
for(j=0;j<i;j++)
{
for(k=0;k<j;k++)
x=x+2; (此語句為頻度最大的語句,可算出f(n)=n^3,寫出時間復雜度T(n)=O(n^3))
}
}
解:當i=m, j=k的時候,內層循環的次數為k當i=m時, j 可以取 0,1,...,m-1 , 所以這里最內循環共進行了0+1+...+m-1=(m-1)m/2次所以,i從0取到n, 則循環共進行了: 0+(1-1)*1/2+...+(n-1)n/2=n(n+1)(n-1)/6所以時間復雜度為O(n^3).
O(log2n ) (此為例外情況,難以算出T(n),但可以利用技巧直接算出O(f(n))。)
例1:
i=1; ①
while (i<=n)
i=i*2; ②
解: 語句1的頻度是1,
設語句2的頻度是f(n), 則:2^f(n)<=n;f(n)<=log2n
取最大值f(n)= log2n,
T(n)=O(log2n )
有如下復雜度關系
c < log2N < n < n * Log2N < n^2 < n^3 < 2^n < 3^n < n!
其中c是一個常量,如果一個算法的復雜度為c 、 log2N 、n 、 n*log2N ,那么這個算法時間效率比較高 ,如果是 2^n , 3^n ,n!,那么稍微大一些的n就會令這個算法不能動了,居於中間的幾個則差強人意。
1.、算法應該是( B)。
A.程序 B.問題求解步驟的描述 C.要滿足五個基本特性 D. A和C
【解析】:程序不一定滿足有窮性,如死循環、操作系統等,而算法必須有窮。算法代表了對問題求解步驟的描述,而程序則是算法在計算機上的特定的實現。
2.某算法的時間復雜度為O(n2),表明該算法的( C)。
A.問題規模是n2 B.執行時間等於n2
C.執行時間與n2成正比 D.問題規模與n2成正比
【解析】:時間復雜度為O(n2),說明算法的執行時間T(n)<=c * n2(c為比例常數),即T(n)=O(n2),時間復雜度T(n)是問題規模n的函數,其問題規模仍然是n而不是n2。
3.以下算法的時間復雜度為( D)。
void fun(int n) {
int i=l;
while(i<=n)
i=i*2;
}
A. O(n) B. O(n2) C. O(nlog2n) D. O(log2n)
【解析】基本運算是i=i*2,設其執行時間為T(n),則2T(n)<=n,即T(n)<=log2n=O(log2n)。
4.【2011年計算機聯考真題】
設n是描述問題規模的非負整數,下面程序片段的時間復雜度是(A)。
x=2;
while(x<n/2)
x=2*x;
A. O(log2n) B. O(n) C. O(nlog2n) D. O(n2)
【解析】:在程序中,執行頻率最高的語句為“x=2*x”。設該語句共執行了 t次,則2t+1=n/2,故t=log2(n/2)-1=log2n-2,得 T(n)=O(log2n)。
5.【2012年計算機聯考真題】
求整數n (n>=0)階乘的算法如下,其時間復雜度是( B)。
int fact(int n){
if (n<=l) return 1;
return n*fact(n-1);
}
A. O(log2n) B. O(n) C. O(nlog2n) D. O(n2)
【解析】:本題是求階乘n!的遞歸代碼,即n*(n-1)*...*1共執行n次乘法操作,故T(n)=O(n)
6.有以下算法,其時間復雜度為( )。
void fun (int n){
int i=0;
while(i*i*i<=n)
i++;
}
7.程序段
for(i=n-l;i>l;i--)
for(j=1;j<i;j++)
if (A[j]>A[j+l])
A[j]與 A[j+1]對換;
其中n為正整數,則最后一行的語句頻度在最壞情況下是(D)。
A. O(n) B. O(nlogn) C. O(n3) D. O(n2)
【解析】:當所有相鄰元素都為逆序時,則最后一行的語句每次都會執行。此時,
8.以下算法中加下划線語句的執行次數為(A)。
int m=0, i, j;
for(i=l;i<=n;i++)
for(j=1;j<=2 * i;j++)
m++;
A. n(n+1) B. n C. n+1 D. n2
【解析】:
9.下面說法錯誤的是( B)。
Ⅰ.算法原地工作的含義是指不需要任何額外的輔助空間
Ⅱ.在相同的規模n下,復雜度O(n)的算法在時間上總是優於復雜度O(2n)的算法
Ⅲ.所謂時間復雜度是指最壞情況下,估算算法執行時間的一個上界
Ⅳ.同一個算法,實現語言的級別越高,執行效率就越低
A. Ⅰ B. Ⅰ、Ⅱ C. Ⅰ、Ⅳ D. Ⅲ
【解析】:Ⅰ,算法原地工作是指算法所需的輔助空間是常量。Ⅱ,題中是指算法的時間復雜度,不要想當然認為是程序(該算法的實現)的具體執行時間,而賦予n—個特殊的值。時間復雜度為O(n)的算法,必然總是優於時間復雜度為O(2n)的算法。Ⅲ,時間復雜度總是考慮在最壞情況下的時間復雜度,以保證算法的運行時間不會比它更長。Ⅳ為嚴蔚敏教材的原話。
二、綜合應用題
2.分析以下各程序段,求出算法的時間復雜度。
// 程序段①
i=l;k=0;
while(i<n-l)
{
k=k+10*i;
i++;
}
【解析】:基本語句是k=k+10*i,共執行了n-2次,所以T(n)=O(n)。
// 程序段②
y=0;
while((y+1)*(y+1)<=n)
y=y+1;
【解析】:設循環體共執行T(n)次,每循環一次,循環變量y加1,最終T(n)=y。故(T(n))2<=n,解得 T(n)=O(n1/2)。
// 程序段③
for(i=l;i<=n;i++)
for(j =1;j <=i;j ++)
for(k=l;k<=j;k++)
x++;
【解析】:
// 程序段④
for(i=0;i<n;i++)
for(j=0;j<m;j++)
a[i] [j]=0;
【解析】:a[i][j]=0是基本語句,內循環執行m次,外循環執行n次,共執行了 m*n次,所以 T(m, n)=O(m*n)0