數據結構之時間複雜度的計算

計算過程:

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

推導示例

1、常數階

首先順序結構的時間複雜度。下面這個算法,是利用高斯定理計算1,2,……n個數的和。

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

這個算法的運行次數函數是f (n) =3。 根據我們推導大0階的方法,

  1. 第一步就是把常數項3 改爲1。
  2. 在保留最高階項時發現,它根本沒有最高階項
  3. 所以這個算法的時間複雜度爲0(1)。
2、線性階

線性階的循環結構會複雜很多。要確定某個算法的階次,我們常常需要確定某個特定語句或某個語句集運行的次數。因此,我們要分析算法的複雜度,關鍵就是要分析循環結構的運行情況。
下面這段代碼,它的循環的時間複雜度爲O(n), 因爲循環體中的代碼須要執行n次。

int i; 

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

/*時間複雜度爲O(1)的程序步驟序列*/

}
3、對數階

今天某位18級同學請教我的這個問題,促使我寫了這篇博客。

public class TS {
	public static void main(String[] args) {
		int i=1;
		int n= 100;
		while(i<n) {
			i = i*2;
		}	
}

由於每次count乘以2之後,就距離n更近了一分。 也就是說,有多少個2相乘後大於n,則會退出循環。 由2^x=n 得到x=log2n\log_2 n。 所以這個循環的時間複雜度爲O(log2n\log_2 n)。

4、平方階

例如:冒泡排序。和下面段程序

int i, j; 

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

  for(j = 0; j < n; j++){

/*時間複雜度爲O(1)的程序步驟序列*/

  }

}

而對於外層的循環,不過是內部這個時間複雜度爲O(n)的語句,再循環n次。 所以這段代碼的時間複雜度爲O(n2n^2)。
如果外循環的循環次數改爲了m,時間複雜度就變爲O(m*n)。
所以我們可以總結得出,循環的時間複雜度等於循環體的複雜度乘以該循環運行的次數。
那麼下面這個循環嵌套,它的時間複雜度是多少呢?

int i, j; 
 
for(i = 0; i < n; i++){
 
  for(j = i; j < n; j++){ /*注意j = i而不是0*/
 
  /*時間複雜度爲O(1)的程序步驟序列*/
 
  }
 
}

由於當i=0時,內循環執行了n次,當i = 1時,執行了n-1次,……當i=n-1時,執行了1次。所以總的執行次數爲:

n+(n1)+(n2)n + (n - 1) + (n - 2) …… +1+1 = n(n+1)2\frac{n(n+1)}{2} = n22\frac{n^2}{2} + n2\frac{n}{2}

用我們推導大O階的方法

  1. 沒有加法常數不予考慮;
  2. 只保留最高階項,因此保留時n22\frac{n^2}{2};
  3. 去除這個項相乘的常數,也就是去除1/2,最終這段代碼的時間複雜度爲O(n2n^2)。

從這個例子,我們也可以得到一個經驗,其實理解大0推導不算難,難的是對數列的一些相關運算,這更多的是考察你的數學知識和能力。

4、立方階

下面例子是一個三重循環嵌套。

int i, j; 
 
for(i = 1; i < n; i++)
 
for(j = 1; j < n; j++)
 
for(j = 1; j < n; j++){
 
/*時間複雜度爲O(1)的程序步驟序列*/
 
 
 
}
5、常見的時間複雜度排序

在這裏插入圖片描述

我們前面已經談到了。O(1)常數階、O(logn)對數階、O(n)線性階、 O(n2n^2)平方階等,像O(n3n^3),過大的n都會使得結果變得不現實。同樣指數階O(2n2^n)和階乘階O(n!)等除非是很小的n值,否則哪怕n 只是100,都是噩夢般的運行時間。所以這種不切實際的算法時間複雜度,一般我們都不去討論。

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