算法時間復雜度求解法【詳細過程說明】


  算法的時間復雜度,是剛開始接觸算法和數據結構時的概念,在真正使用的時候有時候常常忘記它的推導公式。最近准備校招,把二叉樹、排序、查找等這些經典的算法復習了一遍,這次把這些都整理成博客以便以后查看,復習計划接近尾聲,這兩天老是不在狀態,學習圖的時候有點暈乎乎,今天反過頭來把時間復雜度的求解法整理一下,還是頗有收獲,以前很多地方自己存在着理解誤差。希望對大家也有所幫助,有不對的地方還請多指教。

在進行算法分析時,語句總的執行次數T(n)是關於問題規模n的函數,進而分析T(n)隨n的變化情況並確定T(n)的數量級。算法的時間復雜度,也就是算法的時間量度,基座T(n)=O(f(n))。它表示隨問題規模n的增大,算法執行時間的增長率和f(n)的增長率相同,稱作算法的漸進算法時間復雜度,簡稱為時間復雜度。其中f(n)是問題規模n的某個函數。

一般用大寫O()來表示算法的時間復雜度寫法,通常叫做大O記法。

一般情況下,隨着n的增大,T(n)增長最慢的算法為最優算法。

O(1):常數階

O(n):線性階

O(n2):平方階

 

大O推導法:

  1. 用常數1取代運行時間中的所有加法常數
  2. 在修改后的運行函數中,只保留最高階項
  3. 如果最高階項存在且不是1,則去除與這個項相乘的常數

 

常數階:

int sum = 0 ; n = 100;        /*執行一次*/
sum = (1+n)*n/2;             /*執行一次*/
printf("%d",sum);            /*執行一次*/

這個算法的運行次數f(n) = 3,根據推導大O階的方法,第一步是將3改為1,在保留最高階項是,它沒有最高階項,因此這個算法的時間復雜度為O(1);

另外,

int sum = 0 ; n = 100;        /*執行一次*/
sum = (1+n)*n/2;             /*執行第1次*/
sum = (1+n)*n/2;             /*執行第2次*/
sum = (1+n)*n/2;             /*執行第3次*/
sum = (1+n)*n/2;             /*執行第4次*/
sum = (1+n)*n/2;             /*執行第5次*/
sum = (1+n)*n/2;             /*執行第6次*/
sum = (1+n)*n/2;             /*執行第7次*/
sum = (1+n)*n/2;             /*執行第8次*/
sum = (1+n)*n/2;             /*執行第9次*/
sum = (1+n)*n/2;             /*執行第10次*/
printf("%d",sum);            /*執行一次*/

 

上面的兩段代碼中,其實無論n有多少個,本質是是3次和12次的執行差異。這種與問題的大小無關,執行時間恆定的算法,成為具有O(1)的時間復雜度,又叫做常數階。

注意:不管這個常數是多少,3或12,都不能寫成O(3)、O(12),而都要寫成O(1)

此外,對於分支結構而言,無論真假執行的次數都是恆定不變的,不會隨着n的變大而發生變化,所以單純的分支結構(不在循環結構中),其時間復雜度也是O(1)。

 

線性階:

線性階的循環結構會復雜一些,要確定某個算法的階次,需要確定特定語句或某個語句集運行的次數。因此要分析算法的復雜度,關鍵是要分析循環結構的運行情況。

int i;
for(i = 0 ; i < n ; i++){
  /*時間復雜度為O(1)的程序*/      
}

 

對數階:

int count = 1;
while(count < n){
  count = count * 2;
  /*時間復雜度為O(1)的程序*/    
}

 

因為每次count*2后,距離結束循環更近了。也就是說有多少個2 相乘后大於n,退出循環。

數學公式:2x = n    -->     x = log2n

因此這個循環的時間復雜度為O(logn)

 

平方階:

int i;
for(i = 0 ; i < n ; i++){
   for(j = 0 ; j < n ; j++){
    /*時間復雜度為O(1)的程序*/  
    }    
}

 

上面的程序中,對於對於內層循環,它的時間復雜度為O(n),但是它是包含在外層循環中,再循環n次,因此這段代碼的時間復雜度為O(n2)。

int i;
for(i = 0 ; i < n ; i++){
   for(j = 0 ; j < m ; j++){
    /*時間復雜度為O(1)的程序*/  
    }    
}

 

但是,如果內層循環改成了m次,時間復雜度就為O(n*m)

 

再來看一段程序:

int i;
for(i = 0 ; i < n ; i++){
   for(j = i ; j < n ; j++){
    /*時間復雜度為O(1)的程序*/  
    }    
}

 

注意:上面的內層循環j = i ;而不是0

因為i = 0時,內層循環執行了n次,當i=1時,執行了n-1次……當i=n-1時,執行了1次,所以總的執行次數為:

n+(n-1)+(n-1)+...+1 = n(n+1)/2  =  n2/2 + n/2

根據大O推導方法,保留最高階項,n2/2 ,然后去掉這個項相乘的常數,1/2

因此,這段代碼的時間復雜度為O(n2)

 

下面,分析調用函數時的時間復雜度計算方法:

首先,看一段代碼:

int i,j;

void function(int count){
  print(count);  
}

for(i = 0 ; i < n ; i++){
  function (i)  
}

 

函數的時間復雜度是O(1),因此整體的時間復雜度為O(n)。

假如function是這樣的:

void function(int count){
  int j;
  for(j = count ; j < n ;j++){
        /*時間復雜度為O(1)的程序*/
 }
}

 

和第一個的不同之處在於把嵌套內循環放到了函數中,因此最終的時間復雜度為O(n2)

 

再來看一個比價復雜的語句:

n++;                                      /*執行次數為1*/
function(n);                              /*執行次數為n*/
int i,j;
for(i = 0 ; i < n ; i++){                 /*執行次數為nXn*/
  function(i);  
}
for(i = 0 ; i < n ; i++){                /*執行次數為n(n+1)/2*/
  for(j = i ; j < n ; j++){
      /*時間復雜度為O(1)的程序*/  
  }  
}    

 

它的執行次數f(n) = 1 + n + nn(n+1)/+ 3/2n2+3/n+1,

根據推導大O階的方法,最終它的時間復雜度為:O(n2)

 

常見的時間復雜度:

執行次數函數 術語描述
12 O(1) 常數階
2n+3 O(n) 線性階
3n2+2n+1 O(n2) 平方階
5log2n+20 O(log2n) 對數階
2n+3nlog2n+19 O(nlogn) nlog2n階
6n3+2n2+3n+4 O(n3) 立方階
2n O(2n) 指數階

 

 

 

 

 

 

 

 

時間復雜度所耗費的時間是:

O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) <O(2n) < O(n!) <O(nn)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM