大O表示法
用O(n)來體現算法時間複雜度的記法被稱作大O表示法。一般我們我們評估一個算法都是直接評估它的最壞的複雜度。
推導大O階
推導大O階有一下三種規則:
- 用常數1取代運行時間中的所有加法常數
- 只保留最高階項
- 去除最高階的常數
常見算法複雜度樣例
常數階
int sum = 0, n = 10; // 語句執行一次
int sum = (1+n)*n/2; // 語句執行一次
cout << "The sum is : " << sum << endl; //語句執行一次
這樣的一段代碼它的執行次數爲 3 ,然後我們套用規則1,則這個算法的時間複雜度爲 O(1),也就是常數階。
x=91;
y=100;
while (y>0) {
if (x>100) {
x=x-10;
y–;
} else {
x++;
}
}
如上的代碼看上去很大,實際的 T(n) = O(1) 。因爲總共循環運行了 1000 次,但是這段程序的運行是和 n 無關的,所以只是一個常數階的。
對數階
int number = 1; // 語句執行一次
while (number < n) { // 語句執行logn次
number *= 2; // 語句執行logn次
}
上面的算法中,number 每次都放大兩倍,我們假設這個循環體執行了 m 次,那麼 即 m = logn
,所以整段代碼執行次數爲 1 + 2*logn,則 f(n) = logn
,時間複雜度爲 O(logn)。
線性階
int i = 0; // 語句執行一次
while (i < n) { // 語句執行n次
cout << "Current i is " << i << endl; //語句執行n次
i++; // 語句執行n次
}
這個算法中代碼總共執行了 n 次,根據規則 2,因此該算法的時間複雜度是 O(n)。
線性對數階
for (int m=1; m<n; m++) {
int i = 1;
while (i<n) {
i = i * 2;
}
}
該算法的時間複雜度是 O(nlogn)。
平方階
for (int i = 0; i < n; i++) { // 語句執行n次
for (int j = 0; j < n; j++) { // 語句執行n^2次
cout << "I am here!" << endl; // 語句執行n^2
}
}
上面的嵌套循環中,代碼共執行 ,則
。所以該算法的時間複雜度爲 。
立方階
for (int i = 0; i < n; i++) { // 語句執行n次
for (int j = 0; j < n; j++) { // 語句執行n^2次
for (int k = 0; k < n; k++) { // 語句執行n^3次
cout << "I am here!" << endl; // 語句執行n^3
}
}
}
上面的嵌套循環中,代碼共執行 ,則
。所以該算法的時間複雜度爲 。
指數階
function f(n) {
if (n==0) {
return 1;
}
return f(n-1) + f(n-1)
}
如果 n=0,f(n)執行爲 1 次。
如果 n=1,f(n)執行了 f(0)+f(0)=2 次。
如果 n=2,f(n)執行了 f(1)+f(1)=f(0)+f(0)=4 次。
如果 n=3,f(n)執行了 f(2)+f(2)=f(1)+f(1)=8 次。
如果 n=4,f(n)執行了 f(3)+f(3)=f(2)+f(2)=16 次。
以此類推。當 n 趨向無窮大的時候,我們可以推導出 。所以該算法的時間複雜度爲 。
類似的斐波那契數列的時間複雜度也是 。下面借用一個別人繪製的圖片說明。
階乘階
待舉例。
這類算法的時間複雜度爲 。
總結
常見時間複雜度的比較
O(1) < O(logn) < O(n) < O(nlogn) < O(n²) < O(n³) < O(2ⁿ) < O(n!)