1. 程式人生 > >時間複雜度和空間複雜度_簡單易上手

時間複雜度和空間複雜度_簡單易上手

時間複雜度和空間複雜度

  演算法複雜度分為時間複雜度和空間複雜度,一個好的演算法應該具體執行時間短,所需空間少的特點。

  時間複雜度

  隨著計算機硬體和軟體的提升,一個演算法的執行時間是算不太精確的。只能依據統計方法對演算法進行估算。我們拋開硬體和軟體的因素,演算法的好壞直接影響程式的執行時間。

  我們看一個小例子:

     int value = 0;                         // 執行了1次      for (int i = 0; i < n; i++)         //執行了n次
    {     
          value += i;
     }   這個演算法執行了 1 + n 次,如果n無限大,我們可以把前邊的1忽略,也就是說這個演算法執行了n次。   時間複雜度常用大O符號表示,這個演算法的時間複雜度就是O(n).   概念: 一般情況下,演算法的基本操作重複執行的次數是模組n的某一函式f(n),因此,演算法的時間複雜度記做 T(n) = O(f(n))。 隨著模組n的增大,演算法執行的時間增長率f(n)的增長率成正比,所以f(n)越小,演算法 的時間複雜度越低,演算法的效率越高。    計算時間複雜度
  1.    去掉執行時間中的所有加法常數。
  2.    只保留最高階項。
  3.    如果最高階項存在且不是1,去掉與這個最高階相乘的常數得到時間複雜度
  我們看一個例子      for (int i = 0; i < n; i++)
    {           for (int j = i; j < n; j++)
          {                  // do .....           }      }   當 i = 0 時, 裡面的for迴圈執行了n次; 當i = 1時裡面的for迴圈執行了n -  1次; 當i = 2裡裡面的fro執行了n - 2次........   所以執行的次數是   根據我們上邊的時間複雜度計算方法:      1.去掉執行時間中的所有加法常數: 沒有加法常數不用考慮。
     2.只保留最高階項: 只保留       3. 去掉與這個最高階相乘的常數:  去掉  只剩下   最終這個演算法的時間複雜度為   再看一個線性的       for ( int i = 0; i < n; i++)
     {           // do .....      }   因為迴圈要執行n次所以時間複雜度為O(n)   下面貼一個表格
時間複雜度O(n)與輸入規模 n 的關係
函式 / 輸入規模n
1
2 4 8 16
32
1 1 1 1 1 1 1
logn
0 1 2 3 4 5
n
1 2 4 8 16 32
nlogn 0 2 8 24 64 160
n^2
1 4 16 64 256 1024
n^3
1 8 64 512 4096 32768
2^n
2 4 16 256 65536 4294967296
n!
1 2 24 40326 2092278988000 26313X10^33

空間複雜度

  類似於時間複雜度的討論,一個演算法的空間複雜度(Space Complexity) S(n)定義為該演算法所耗費的儲存空間,它也是問題規模n的函式。漸近空間複雜度也常常簡稱為空間複雜度。

  空間複雜度(Space Complexity)是對一個演算法在執行過程中臨時佔用儲存空間大小的量度。
  一個演算法在計算機儲存器上所佔用的儲存空間,包括儲存演算法本身所佔用的儲存空間,演算法的輸入輸出資料所佔用的儲存空間和演算法在執行過程中臨時佔用的儲存空間這三個方面。

  我們在寫程式碼時,完全可以用空間來換取時間。比如說,要判斷某某年是不是閏年,你可能會花一點心思寫一個演算法。也就意味著,每次給一個年份,都是要通過計算得到是否是閏年的結果。不過,還有另一個辦法就是,事先建立一個有2 050個元素的陣列(年數略比現實多一點),然後把所有的年份按下標的數字對應,如果是閏年,此陣列項的值就是1,如果不是值為0。這樣,所謂的判斷某一年是否是閏年,就變成了查詢這個陣列的某一項的值是多少的問題。此時,我們的運算是最小化了,但是硬碟上或者記憶體中需要儲存這2050個0和1。

  1.   演算法的輸入輸出資料所佔用的儲存空間是由要解決的問題決定的,是通過引數表由呼叫函式傳遞而來的,它不隨本演算法的不同而改變。
  2.   儲存演算法本身所佔用的儲存空間與演算法書寫的長短成正比,要壓縮這方面的儲存空間,就必須編寫出較短的演算法。
  3.    演算法在執行過程中臨時佔用的儲存空間隨演算法的不同而異,有的演算法只需要佔用少量的臨時工作單元,而且不隨問題規模的大小而改變,我們稱這種演算法是“就地"進行的,是節省儲存的演算法,如這一節介紹過的幾個演算法都是如此;有的演算法需要佔用的臨時工作單元數與解決問題的規模n有關,它隨著n的增大而增大,當n較大時,將佔用較多的儲存單元,例如將快速排序和歸併排序演算法就屬於這種情況。

通過一筆空間上的開銷來換取計算時間的小技巧。到底哪一個好,其實要看你用在什麼地方。

演算法的空間複雜度通過計算演算法所需的儲存空間實現。演算法空間複雜度的計算公式記作:S(n)= O(f(n)),其中,n為問題的規模,f(n)為語句關於n所佔儲存空間的函式。

一般情況下,一個程式在機器上執行時,除了需要儲存程式本身的指令、常數、變數和輸入資料外,還需要儲存對資料操作的儲存單元。若輸入資料所佔空間只取決於問題本身,和演算法無關,這樣只需要分析該演算法在實現時所需的輔助單元即可。若演算法執行時所需的輔助空間相對於輸入資料量而言是個常數,則稱此演算法為原地工作,空間複雜度為O(1)。

  關於O(1)的問題, O(1)是說資料規模和臨時變數數目無關,並不是說僅僅定義一個臨時變數。舉例:無論資料規模多大,我都定義100個變數,這就叫做資料規模和臨時變數數目無關。就是說空間複雜度是O(1)。

總結一下

  通常,我們都使用“時間複雜度”來指執行時間的需求,使用“空間複雜度”指空間需求。當不用限定詞地使用“複雜度”時,通常都是指時間複雜度。

  對於一個演算法,其時間複雜度和空間複雜度往往是相互影響的。當追求一個較好的時間複雜度時,可能會使空間複雜度的效能變差,即可能導致佔用較多的儲存空間;反之,求一個較好的空間複雜度時,可能會使時間複雜度的效能變差,即可能導致佔用較長的執行時間。另外,演算法的所有效能之間都存在著或多或少的相互影響。因此,當設計一個演算法(特別是大型演算法)時,要綜合考慮演算法的各項效能,演算法的使用頻率,演算法處理的資料量的大小,演算法描述語言的特性,演算法執行的機器系統環境等各方面因素,才能夠設計出比較好的演算法。