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

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


排序法

最差時間分析平均時間複雜度穩定度空間複雜度
冒泡排序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)
希爾排序OO不穩定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的某個函數,用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)
   基本操作即算法中的每條語句(以;號作爲分割),語句的執行次數也叫做語句的頻度。在做算法分析時,一般默認爲考慮最壞的情況。

2. 計算出T(n)的數量級
   求T(n)的數量級,只要將T(n)進行如下一些操作:
   忽略常量、低次冪和最高次冪的係數

   令f(n)=T(n)的數量級。

3. 用大O來表示時間複雜度
   當n趨近於無窮大時,如果lim(T(n)/f(n))的值爲不等於0的常數,則稱f(n)是T(n)的同數量級函數。記作T(n)=O(f(n))。


一個示例:
(1) int num1, num2;
(2) for(int i=0; i<n; i++){
(3)     num1 += 1;
(4)     for(int j=1; j<=n; j*=2){
(5)         num2 += num1;
(6)     }
(7) }


分析:
1.
語句int num1, num2;的頻度爲1;
語句i=0;的頻度爲1;
語句i<n; i++; num1+=1; j=1; 的頻度爲n;
語句j<=n; j*=2; num2+=num1;的頻度爲n*log2n;
T(n) = 2 + 4n + 3n*log2n

2.
忽略掉T(n)中的常量、低次冪和最高次冪的係數
f(n) = n*log2n

3.
lim(T(n)/f(n)) = (2+4n+3n*log2n) / (n*log2n)
                    = 2*(1/n)*(1/log2n) + 4*(1/log2n) + 3

當n趨向於無窮大,1/n趨向於0,1/log2n趨向於0
所以極限等於3。

T(n) = O(n*log2n)

簡化的計算步驟

再來分析一下,可以看出,決定算法複雜度的是執行次數最多的語句,這裏是num2 += num1,一般也是最內循環的語句。

並且,通常將求解極限是否爲常量也省略掉?

於是,以上步驟可以簡化爲:
1. 找到執行次數最多的語句
2. 計算語句執行次數的數量級
3. 用大O來表示結果


繼續以上述算法爲例,進行分析:
1.
執行次數最多的語句爲num2 += num1

2.
T(n) = n*log2n
f(n) = n*log2n

3.
// lim(T(n)/f(n)) = 1
T(n) = O(n*log2n)


--------------------------------------------------------------------------------
一些補充說明
最壞時間複雜度
   算法的時間複雜度不僅與語句頻度有關,還與問題規模及輸入實例中各元素的取值有關。一般不特別說明,討論的時間複雜度均是最壞情況下的時間複雜度。這就保證了算法的運行時間不會比任何更長。

求數量級
即求對數值(log),默認底數爲10,簡單來說就是“一個數用標準科學計數法表示後,10的指數”。例如,5000=5x10 3 (log5000=3) ,數量級爲3。另外,一個未知數的數量級爲其最接近的數量級,即最大可能的數量級。

求極限的技巧
要利用好1/n。當n趨於無窮大時,1/n趨向於0


--------------------------------------------------------------------------------
一些規則(引自:時間複雜度計算 )
1) 加法規則
T(n,m) = T1(n) + T2(n) = O (max ( f(n), g(m) )

2) 乘法規則

T(n,m) = T1(n) * T2(m) = O (f(n) * g(m))

3) 一個特例(問題規模爲常量的時間複雜度)
在大O表示法裏面有一個特例,如果T1(n) = O(c), c是一個與n無關的任意常數,T2(n) = O ( f(n) ) 則有
T(n) = T1(n) * T2(n) = O ( c*f(n) ) = O( f(n) )

也就是說,在大O表示法中,任何非0正常數都屬於同一數量級,記爲O(1)。

4) 一個經驗規則
複雜度與時間效率的關係:
c < log2n < n < n*log2n < n2 < n3 < 2n < 3n < n! (c是一個常量)
|--------------------------|--------------------------|-------------|
         較好                     一般              較差
其中c是一個常量,如果一個算法的複雜度爲c 、 log2n 、n 、 n*log2n,那麼這個算法時間效率比較高 ,如果是 2n , 3n ,n!,那麼稍微大一些的n就會令這個算法不能動了,居於中間的幾個則差強人意。


--------------------------------------------------------------------------------------------------
複雜情況的分析

以上都是對於單個嵌套循環的情況進行分析,但實際上還可能有其他的情況,下面將例舉說明。

1.並列循環的複雜度分析
將各個嵌套循環的時間複雜度相加。

例如:

  for (i=1; i<=n; i++)
      x++;

  for (i=1; i<=n; i++)
      for (j=1; j<=n; j++)
          x++;

解:
第一個for循環
T(n) = n
f(n) = n
時間複雜度爲Ο(n)

第二個for循環
T(n) = n2
f(n) = n2
時間複雜度爲Ο(n2)

整個算法的時間複雜度爲Ο(n+n2) = Ο(n2)。

2.函數調用的複雜度分析
例如:
public void printsum(int count){
   int sum = 1;
   for(int i= 0; i<n; i++){
      sum += i;
   }  
   System.out.print(sum);
}

分析:
記住,只有可運行的語句纔會增加時間複雜度,因此,上面方法裏的內容除了循環之外,其餘的可運行語句的複雜度都是O(1)。
所以printsum的時間複雜度 = for的O(n)+O(1) = 忽略常量 = O(n)

*這裏其實可以運用公式 num = n*(n+1)/2,對算法進行優化,改爲:
public void printsum(int count){
   int sum = 1;
   sum = count * (count+1)/2;  
   System.out.print(sum);
}
這樣算法的時間複雜度將由原來的O(n)降爲O(1),大大地提高了算法的性能。


3.混合情況(多個方法調用與循環)的複雜度分析
例如:
public void suixiangMethod(int n){
   printsum(n);//1.1
   for(int i= 0; i<n; i++){
      printsum(n); //1.2
   }
   for(int i= 0; i<n; i++){
      for(int k=0; k
       System.out.print(i,k); //1.3
     }
 }
suixiangMethod 方法的時間複雜度需要計算方法體的各個成員的複雜度。
也就是1.1+1.2+1.3 = O(1)+O(n)+O(n2) ----> 忽略常數 和 非主要項 == O(n2)

--------------------------------------------------------------------------------------------------
更多的例子

O(1)
交換i和j的內容
temp=i;
i=j;
j=temp;                    

以上三條單個語句的頻度爲1,該程序段的執行時間是一個與問題規模n無關的常數。算法的時間複雜度爲常數階,記作T(n)=O(1)。如果算法的執行時間不隨着問題規模n的增加而增長,即使算法中有上千條語句,其執行時間也不過是一個較大的常數。此類算法的時間複雜度是O(1)。

O(n2)
   sum=0;                /* 執行次數1 */
   for(i=1;i<=n;i++)      
      for(j=1;j<=n;j++)
        sum++;       /* 執行次數n2 */
解:T(n) = 1 + n2 = O(n2)

  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) = 2n2-n-1
        T(n) = 2n2-n-1+(n-1) = 2n2-2
        f(n) = n2
        lim(T(n)/f(n)) = 2 + 2*(1/n2) = 2
        T(n) = O(n2).

O(n)    
  a=0;
  b=1;                     ①
  for (i=1;i<=n;i++) ②
  {  
     s=a+b;    ③
     b=a;     ④  
     a=s;     ⑤
  }
解:  語句1的頻度:2,        
        語句2的頻度:n,        
        語句3的頻度:n,        
        語句4的頻度:n,    
        語句5的頻度:n,                                  
        T(n) = 2+4n
        f(n) = n
        lim(T(n)/f(n)) = 2*(1/n) + 4 = 4
        T(n) = O(n).    

O(log2n)
  i=1;       ①
  while (i<=n)
     i=i*2; ②
解: 語句1的頻度是1,  
      設語句2的頻度是t,  則:nt<=n;  t<=log2n
      考慮最壞情況,取最大值t=log2n,
       T(n) = 1 + log2n
       f(n) = log2n
       lim(T(n)/f(n)) = 1/log2n + 1 = 1
       T(n) = O(log2n)

O(n3)
  for(i=0;i<n;i++)
  {  
     for(j=0;j<i;j++)  
     {
        for(k=0;k<j;k++)
           x=x+2;  
     }
  }
解:當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)/2次
T(n) = n(n+1)(n-1)/2 = (n3-n)/2
f(n) = n3
所以時間複雜度爲O(n3)。


--------------------------------------------------------------------------------------------------

參考資料
百度百科——時間複雜度數量級函數



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