桶排序與帶循環的算法時間複雜度分析

在計算算法的時間複雜度時,我們一般採用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();
    }
}


發佈了109 篇原創文章 · 獲贊 14 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章