⑴ 找出演算法中的基本語句;
演算法中執行次數最多的那條語句就是基本語句,通常是最內層迴圈的迴圈體。
⑵ 計算基本語句的執行次數的數量級;
只需保留f(n)中的最高次冪正確即可,可以忽略所有低次冪和最高次冪的係數。
⑶ 用大Ο記號表示演算法的時間效能。
將基本語句執行次數的數量級放入大Ο記號中。
如果演算法中包含巢狀的迴圈,則基本語句通常是最內層的迴圈體,如果演算法中包含並列的迴圈,則將並列迴圈的時間複雜度相加。例如:
for (i=1; i<=n; i++)
x++;
for (i=1; i<=n; i++)
for (j=1; j<=n; j++)
x++;
第一個for迴圈的時間複雜度為Ο(n),第二個for迴圈的時間複雜度為Ο(n²),則整個演算法的時間複雜度為Ο(n+n²)=Ο(n²)。
注、加法原則:T(n)=O(f(n))+O(g(n))=O(max(fn,gn))
常見的演算法時間複雜度由小到大依次為:
Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n²)<Ο(n³)<…<Ο(2^n)<Ο(n!)<O(n^n)
Ο(1)表示基本語句的執行次數是一個常數,一般來說,只要演算法中不存在迴圈語句,其時間複雜度就是Ο(1)。Ο(log2n)、Ο(n)、Ο(nlog2n)、Ο(n2)和Ο(n3)稱為多項式時間,而Ο(2n)和Ο(n!)稱為指數時間。電腦科學家普遍認為前者是有效演算法,把這類問題稱為P類問題,而把後者稱為NP問題。
對於一個迴圈,假設迴圈體的時間複雜度為 O(n),迴圈次數為 m,則這個迴圈的時間複雜度為 O(n×m)。
void aFunc(int n) {
for(int i = 0; i < n; i++) { // 迴圈次數為 n
printf("Hello, World!\n"); // 迴圈體時間複雜度為 O(1)
}
}
此時時間複雜度為 O(n × 1),即 O(n)。
對於多個迴圈,假設迴圈體的時間複雜度為 O(n),各個迴圈的迴圈次數分別是a, b, c...,則這個迴圈的時間複雜度為 O(n×a×b×c...)。分析的時候應該由裡向外分析這些迴圈。
void aFunc(int n) { for(int i = 0; i < n; i++) { // 迴圈次數為 n for(int j = 0; j < n; j++) { // 迴圈次數為 n printf("Hello, World!\n"); // 迴圈體時間複雜度為 O(1) } } }
此時時間複雜度為 O(n × n × 1),即 O(n^2)。
對於順序執行的語句或者演算法,總的時間複雜度等於其中最大的時間複雜度。
void aFunc(int n) {
// 第一部分時間複雜度為 O(n^2)
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
printf("Hello, World!\n");
}
}
// 第二部分時間複雜度為 O(n)
for(int j = 0; j < n; j++) {
printf("Hello, World!\n");
}
}
此時時間複雜度為 max(O(n^2), O(n)),即 O(n^2)。
對於條件判斷語句,總的時間複雜度等於其中 時間複雜度最大的路徑 的時間複雜度。
void aFunc(int n) {
if (n >= 0) {
// 第一條路徑時間複雜度為 O(n^2)
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
printf("輸入資料大於等於零\n");
}
}
} else {
// 第二條路徑時間複雜度為 O(n)
for(int j = 0; j < n; j++) {
printf("輸入資料小於零\n");
}
}
}
此時時間複雜度為 max(O(n^2), O(n)),即 O(n^2)。
時間複雜度分析的基本策略是:從內向外分析,從最深層開始分析。如果遇到函式呼叫,要深入函式進行分析。