算法的復雜度--[轉]


一、算法的時間復雜度

1、概述

  算法的時間復雜度使用大O表示法,如O(1)、O(n)、O(logn)、O(n²)、O(n³)、O(2ⁿ)、O(n!)、O(√n)等,分別可以稱為常數階、線性階、對數階、平方階、立方階、指數階、階乘階、平方根階。

  推導大O階可以遵循以下規則:
   ①. 用常數1來取代運行時間中所有加法常數。
   ②. 修改后的運行次數函數中,只保留最高階項。
   ③. 如果最高階項存在且不是1,則去除與這個項相乘的常數。

2、常見時間復雜度

  ①、常數階

  如下算法的運行的次數的函數為f(n)=3,根據推導大O階的規則1,我們需要將常數3改為1,則這個算法的時間復雜度為O(1),即常數階。

void test() { int sum = 0, x = 100; //執行一次 
    sum = (1 + x)*x / 2; //執行一次 
    System.out.println(sum); //執行一次 
}

  當算法的執行時間不隨着輸入大小n的增加而增長,即使算法中有上千條語句,其執行時間也不過是一個較大的常數,所以其算法的時間復雜度是常數階,即O(1)。

void func() { int x = 90, y = 100; while (y > 0) { if (y >= 0) { x = x - 10; y--; } } }

  ②、線性階

  線性階的代表有一重循環,隨着輸入n的增大,算法執行時間線性增長。比如下面的算法,在循環體中的代碼執行了n次,因此時間復雜度為O(n)。

void func(int n) { for (int i = 0; i<n; i++) { //時間復雜度為O(1)的算法
 ... } }

  以下是求階乘算法,可以看到n沒增加1就多執行一次遞歸,所以其時間復雜度是線性的O(n)。

long long Fact(unsigned int n)
{
    if (n == 0)
        return 1;
    return n * Fact(n - 1);
}

   ③、對數階

  如下代碼,number初始值為1,並以每次乘2的變化越來越接近n,假設循環執行的次數為x,則由2^x = n可以得出x = log₂n,所以這個算法的時間復雜度為O(logn),類似的二分法查找算法的時間復雜度也是O(logn)。

void func(int n) { int number = 1; while (number < n) { number = number * 2; //時間復雜度為O(1)的算法
 ... } }

  ④、平方階

  如下兩層的嵌套循環的時間復雜度即為O(n²):

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

  又如下面的冒泡排序算法,內循環中代碼執行的次數為:n²/2 - n/2,推導大O階的規則的第二條:只保留最高階,因此保留n²/2,再根據第三條去掉和這個項的常數,即去掉1/2,所以冒泡算法的時間復雜度為O(n²)。

void BubbleSort(int ary[], unsigned int n) { for (unsigned int i = 1; i < n; i++) { for (unsigned int j = i; j < n; j++) { if (ary[j] < ary[i - 1]) { int temp = ary[i - 1]; ary[i - 1] = ary[j]; ary[j] = temp; } } } }

  ⑤、立方階

  如下所示的三層嵌套循環的時間復雜度為O(n³):

for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { //時間復雜度為O(1)的算法
 ... } } }

  3、時間復雜度與效率

  O(logn)、O(n)、O(nlogn )隨着n的增加,復雜度提升不大,因此這些復雜度屬於效率高的算法,而O(2ⁿ)和O(n!)當n增加到50時,復雜度就突破十位數了,這種效率極差的復雜度最好不要出現在程序中。

  常用的時間復雜度按照耗費的時間從小到大依次是:O(logn) < O(n) < O(nlogn) < O(n²) < O(n³) < O(2ⁿ) < O(n!),下面是一個更加直觀的圖: 

  

  二、算法的空間復雜度

  算法的空間復雜度只考慮在運行過程中為局部變量分配的存儲空間的大小,它包括為參數表中形參變量分配的存儲空間和為在函數體中定義的局部變量分配的存儲空間兩個部分。

  類似時間復雜度,若算法執行時所需要的存儲空間相對於輸入n而言是一個常數,則算法的空間復雜度為O(1)。如下算法要分配的空間有n,j,i,k,即f(n) = 4,4為常數,所以空間復雜度是O(1)。

void func(int n) { int j = 100; for (int i = 0; i < n; i++) { int k = i * k; } }

  若一個算法是遞歸算法,由於遞歸需要壓棧,所以其分配的空間為一次調用所分配的臨時存儲空間的大小乘以被調用的次數,所以遞歸算法的空間復雜度為1 + 遞歸調用的次數。比如下面的二分法查找算法的遞歸方式的空間復雜度為O(logn):

 

int HalfFindValue(int ary[], const int& value, int low, int high) { if (low > high) //未找到
        return -1; int mid = (low + high) / 2; if (ary[mid] == value) return mid; else if (ary[mid] > value) return HalfFindValue(ary, value, low, mid - 1); else
        return HalfFindValue(ary, value, mid + 1, high); }

  對於斐波那契數列,如果按照下面普通的遞歸算法,根據圖示,遞歸進入的次數與二叉樹的結點個數相同,二叉樹的深度為n,假設為滿二叉樹的話其結點個數為2ⁿ - 1,故其時間復雜度為O(2ⁿ)。而其空間復雜度可以看出與二叉樹的深度相關,所以為O(n)。

  

long long Fib(int n) { if (n <= 1) return 1; else
        return Fib(n - 1) + Fib(n - 2); }

  由上可知,對於斐波那契數列使用遞歸算法求解的話其時間復雜度是效率極低的,它做了很多重復性的操作,可以使用下列的for循環代替它,使時間復雜度降低為O(n):

long long Fibonacci(unsigned n) { if (n <= 1) return 1; long long llOne = 0; long long llTwo = 1; long long llFib = 0; for (unsigned int i = 2; i <= n; ++i) { llFib = llOne + llTwo; llOne = llTwo; llTwo = llFib; } return llFib; }

 

 以上轉載和參考自:CSDN劉望舒的專欄:算法(一)時間復雜度,地址:https://blog.csdn.net/itachi85/article/details/54882603。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



轉:算法的空間復雜度 算法復雜度3 算法的復雜度 算法的復雜度 關於算法復雜度 各種算法復雜度 算法及其復雜度 什么是算法的復雜度? 關於算法復雜度 算法的復雜度:
 
粤ICP备14056181号  © 2014-2021 ITdaan.com