在計算算法的時間複雜度時,我們一般採用BigO函數。BigO函數中只保留最有價值的函數組成部分,去掉係數,去掉常數。例如:O(a*n^2+b*n+1)=O(n^2)。同時我們在算法分析時會盡量選擇最接近的BigO函數。比如快速排序(QuickSort)和歸併(MergeSort)的算法時間複雜度的上限可以是O(n^2),也可以是O(n*lgn),但我們會選擇O(n*lgn),因爲它最接近。
簡單的算法,很多時候,通過直覺我們就可以得出它的時間複雜度。比如看到下面的語句,如果doSomething時間複雜度是O(1),那麼整個算法的時間複雜度是O(n^2)。
for i in range(n):
for j in range(n):
doSomething2
看到下面的語句,那麼整個算法的時間複雜度是O(n)。
for i in range(n):
doSthing1
但有時直覺是不對的,比如桶排序算法,裏面有雙重循環,但算法的時間複雜度實際上是線性的。在詳細研究桶排序算法之前,我們需要首先掌握如何分析while(或for)循環的時間複雜度。例如下面的算法:
while S1:
S2
S1和S2可以是時間複雜度非O(1)的複合語句。假如這個循環的執行次數是n,那麼S2語句將會執行n次,S1語句會執行n+1次。(for循環同理),那麼整個算法的時間複雜度就是O(S1*(n+1))或者O(S2*n),取兩者的最大值。對於簡單的算法S1*(n+1)無意義,例如下面的算法,時間複雜度O(n+1)=O(n)。但在複雜的算法,如桶排序算法中,O(n+1)中的常數1就有意義。
for i in range(n):
i+1
下面是桶排序算法。首先憑直覺,第12和13行是雙重循環,外重循環次數m,內重循環buckets[j]的最大值可能爲n,那麼算法的時間複雜度是O(m*n);但實際它不是最接近的BigO函數。我們詳細分析一下。因爲總共只有n個元素,實際上第14行只會執行n次。再看13行,內層循環執行(buckets[j]+1)次,外層循環執行m次,那麼整個12和13行會執行的次數爲(buckets[0]+1)+(buckets[1]+1)+...+(buckets[m-1]+1)=n+m。所以整個算法的時間複雜度是O(m+n),而不是直覺的O(m*n)。
01
02 import random
03
04 def bucketSort(alist, n, buckets, m):
05 for i in range(m):
06 buckets[i]=0
07
08 for i in range(n):
09 buckets[alist[i]] += 1
10
11 i=0
12 for j in range(m):
13 for k in range(buckets[j]):
14 alist[i]=j
15 i+=1
16
17 if __name__ == '__main__':
18 m=10
19 n=40
20 alist=[random.randrange(0,m) for i in range(n)]
21 buckets=[0] * 10
22 print('before sort')
23 print(alist)
24 bucketSort(alist,n,buckets,m)
25 print('after sort')
26 print(alist)
27
28
下面是桶排序算法一次執行的輸出結果。桶排序只適合特定類型的排序。
before sort
[7, 1, 6, 4, 6, 0, 2, 9, 3, 9, 6, 3, 9, 1, 5, 1, 1, 6, 0, 8, 2, 0, 1, 0, 7, 9, 1, 5, 7, 8, 7, 4, 7, 0, 0, 7, 8, 8, 9, 3]
after sort
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9]
參考文獻:
Data Structures and Algorithms with Object-Oriented Design Patterns in Python
P.S.正文部分到此結束,前面Python程序的行號由下面程序生成,是我以前寫的一個程序,留個紀念。
import java.util.*;
import java.io.*;
public class Number{
public static void main(String[] args ) throws Exception{
long start=System.currentTimeMillis();
System.out.println ("start time:"+start);
if(args.length!=2)
{
System.out.println("usage: java Number <infile> <outFile>");
return;
}
BufferedReader inReader = new BufferedReader(new FileReader(args[0]));
BufferedWriter outWriter = new BufferedWriter(new FileWriter(args[1]));
int count=0;
String line=null;
do{
line = inReader.readLine();
if(line!=null){
count++;
}
else{
break;
}
}
while(true);
if(count<=0) {
System.out.println(args[0] + " has zero line");
return;
}
String strCount=Integer.toString(count);
//System.out.println("strCount=" +strCount);
int width = strCount.length();
//System.out.println("width=" +width);
inReader.close();
count=0;
inReader = new BufferedReader(new FileReader(args[0]));
do{
line = inReader.readLine();
if(line!=null){
count++;
line = addLeadingZero(Integer.toString(count),width) + " " +line ;
outWriter.write(line,0,line.length());
outWriter.newLine();
}
else{
break;
}
}
while(true);
inReader.close();
outWriter.close();
long end=System.currentTimeMillis();
System.out.println ("end time:"+end);
System.out.println ("total second:"+(end-start)/1000);
}
public static String addLeadingZero(String str,int width){
int len = str.length();
if(len>=width) return str;
width = width - len;
StringBuilder b=new StringBuilder(str);
for(;width>0;width--)
{
b.insert(0,'0');
}
return b.toString();
}
}