常用排序算法總結——Java語言實現

常用排序算法:冒泡排序,選擇排序,插入排序,稀爾排序,快速排序,歸併排序,堆排序,桶式排序,基數排序

目錄

一、冒泡排序(BubbleSort)

二、選擇排序

三、插入排序(Insertion Sort)

四、希爾排序

五、快速排序(Quick Sort)

六、歸併排序

七、堆排序(Heap Sort)

八、桶式排序

九、基數排序

十、幾種排序算法的比較和選擇 

 

排序簡介 
排序是數據處理中經常使用的一種重要運算,在計算機及其應用系統中,花費在排序上的時間在系統運行時間中佔有很大比重;並且排序本身對推動算法分析的發展也起很大作用。

學習重點 
1、掌握排序的基本概念和各種排序方法的特點,並能加以靈活應用;
2、掌握插入排序(直接插入排序、折半插入排序、希爾排序)、交換排序(起泡排序、快速排序)、選擇排序(直接選擇排序、堆排序)、二路歸併排序的方法及其性能分析方法;
3、瞭解基數排序方法及其性能分析方法。

排序(sort)或分類
1.被排序對象--文件
  被排序的對象--文件由一組記錄組成。
  記錄則由若干個數據項(或域)組成。其中有一項可用來標識一個記錄,稱爲關鍵字項。該數據項的值稱爲關鍵字(Key)。
  注意:
     在不易產生混淆時,將關鍵字項簡稱爲關鍵字。

2.排序運算的依據--關鍵字
     用來作排序運算依據的關鍵字,可以是數字類型,也可以是字符類型。
     關鍵字的選取應根據問題的要求而定。
【例】在高考成績統計中將每個考生作爲一個記錄。每條記錄包含准考證號、姓名、各科的分數和總分數等項內容。若要惟一地標識一個考生的記錄,則必須用"准考證號"作爲關鍵字。若要按照考生的總分數排名次,則需用"總分數"作爲關鍵字。

排序的穩定性
     在待排序的文件中,若存在多個關鍵字相同的記錄,經過排序後這些具有相同關鍵字的記錄之間的相對次序保持不變,該排序方法是穩定的;若具有相同關鍵字的記錄之間的相對次序發生變化,則稱這種排序方法是不穩定的
  注意:
     排序算法的穩定性是針對所有輸入實例而言的。即在所有可能的輸入實例中,只要有一個實例使得算法不滿足穩定性要求,則該排序算法就是不穩定的。

排序方法的分類
1.按是否涉及數據的內、外存交換分

     在排序過程中,若整個文件都是放在內存中處理,排序時不涉及數據的內、外存交換,則稱之爲內部排序(簡稱內排序);反之,若排序過程中要進行數據的內、外存交換,則稱之爲外部排序
  注意:
     ① 內排序適用於記錄個數不很多的小文件
     ② 外排序則適用於記錄個數太多,不能一次將其全部記錄放人內存的大文件。
2.按策略劃分內部排序方法
     可以分爲五類:插入排序、選擇排序、交換排序、歸併排序和分配排序。

排序算法分析
1.排序算法的基本操作

     大多數排序算法都有兩個基本的操作:
  (1) 比較兩個關鍵字的大小;
  (2) 改變指向記錄的指針或移動記錄本身。
  注意:
     第(2)種基本操作的實現依賴於待排序記錄的存儲方式。

2.待排文件的常用存儲方式
(1) 以順序表(或直接用向量)作爲存儲結構
    排序過程:對記錄本身進行物理重排(即通過關鍵字之間的比較判定,將記錄移到合適的位置)

(2) 以鏈表作爲存儲結構
  排序過程:無須移動記錄,僅需修改指針。通常將這類排序稱爲鏈表(或鏈式)排序;

(3) 用順序的方式存儲待排序的記錄,但同時建立一個輔助表(如包括關鍵字和指向記錄位置的指針組成的索引表)
  排序過程:只需對輔助表的表目進行物理重排(即只移動輔助表的表目,而不移動記錄本身)。適用於難於在鏈表上實現,仍需避免排序過程中移動記錄的排序方法。

3.排序算法性能評價
(1) 評價排序算法好壞的標準
  評價排序算法好壞的標準主要有兩條:
     ① 執行時間和所需的輔助空間
     ② 算法本身的複雜程度

(2) 排序算法的空間複雜度
  若排序算法所需的輔助空間並不依賴於問題的規模n,即輔助空間是O(1),則稱之爲就地排序(In-PlaceSou)。
  非就地排序一般要求的輔助空間爲O(n)。

(3) 排序算法的時間開銷
  大多數排序算法的時間開銷主要是關鍵字之間的比較和記錄的移動。有的排序算法其執行時間不僅依賴於問題的規模,還取決於輸入實例中數據的狀態。
 

按平均時間將排序分爲四類:

(1)平方階(O(n2))排序
     一般稱爲簡單排序,例如直接插入、直接選擇和冒泡排序;

(2)線性對數階(O(nlgn))排序
     如快速、堆和歸併排序;

(3)O(n1+£)階排序
     £是介於0和1之間的常數,即0<£<1,如希爾排序;

(4)線性階(O(n))排序
     如桶、箱和基數排序。

各種排序方法比較

     簡單排序中直接插入最好,快速排序最快,當文件爲正序時,直接插入和冒泡均最佳。

影響排序效果的因素

     因爲不同的排序方法適應不同的應用環境和要求,所以選擇合適的排序方法應綜合考慮下列因素:
  ①待排序的記錄數目n;
  ②記錄的大小(規模);
  ③關鍵字的結構及其初始狀態;
  ④對穩定性的要求;
  ⑤語言工具的條件;
  ⑥存儲結構;
  ⑦時間和輔助空間複雜度等。

不同條件下,排序方法的選擇

(1)若n較小(如n≤50),可採用直接插入或直接選擇排序。
     當記錄規模較小時,直接插入排序較好;否則因爲直接選擇移動的記錄數少於直接插人,應選直接選擇排序爲宜。
(2)若文件初始狀態基本有序(指正序),則應選用直接插人、冒泡或隨機的快速排序爲宜;
(3)若n較大,則應採用時間複雜度爲O(nlgn)的排序方法:快速排序、堆排序或歸併排序。
     快速排序是目前基於比較的內部排序中被認爲是最好的方法,當待排序的關鍵字是隨機分佈時,快速排序的平均時間最短;
     堆排序所需的輔助空間少於快速排序,並且不會出現快速排序可能出現的最壞情況。這兩種排序都是不穩定的。
     若要求排序穩定,則可選用歸併排序。但本章介紹的從單個記錄起進行兩兩歸併的  排序算法並不值得提倡,通常可以將它和直接插入排序結合在一起使用。先利用直接插入排序求得較長的有序子文件,然後再兩兩歸併之。因爲直接插入排序是穩定的,所以改進後的歸併排序仍是穩定的。

4)在基於比較的排序方法中,每次比較兩個關鍵字的大小之後,僅僅出現兩種可能的轉移,因此可以用一棵二叉樹來描述比較判定過程。
     當文件的n個關鍵字隨機分佈時,任何藉助於"比較"的排序算法,至少需要O(nlgn)的時間。
     箱排序和基數排序只需一步就會引起m種可能的轉移,即把一個記錄裝入m個箱子之一,因此在一般情況下,箱排序和基數排序可能在O(n)時間內完成對n個記錄的排序。但是,箱排序和基數排序只適用於像字符串和整數這類有明顯結構特徵的關鍵字,而當關鍵字的取值範圍屬於某個無窮集合(例如實數型關鍵字)時,無法使用箱排序和基數排序,這時只有藉助於"比較"的方法來排序。
     若n很大,記錄的關鍵字位數較少且可以分解時,採用基數排序較好。雖然桶排序對關鍵字的結構無要求,但它也只有在關鍵字是隨機分佈時才能使平均時間達到線性階,否則爲平方階。同時要注意,箱、桶、基數這三種分配排序均假定了關鍵字若爲數字時,則其值均是非負的,否則將其映射到箱(桶)號時,又要增加相應的時間。
(5)有的語言(如Fortran,Cobol或Basic等)沒有提供指針及遞歸,導致實現歸併、快速(它們用遞歸實現較簡單)和基數(使用了指針)等排序算法變得複雜。此時可考慮用其它排序。
(6)本章給出的排序算法,輸人數據均是存儲在一個向量中。當記錄的規模較大時,爲避免耗費大量的時間去移動記錄,可以用鏈表作爲存儲結構。譬如插入排序、歸併排序、基數排序都易於在鏈表上實現,使之減少記錄的移動次數。但有的排序方法,如快速排序和堆排序,在鏈表上卻難於實現,在這種情況下,可以提取關鍵字建立索引表,然後對索引表進行排序。然而更爲簡單的方法是:引人一個整型向量t作爲輔助表,排序前令t[i]=i(0≤i<n),若排序算法中要求交換R[i]和R[j],則只需交換t[i]和t[j]即可;排序結束後,向量t就指示了記錄之間的順序關係:
        R[t[0]].key≤R[t[1]].key≤…≤R[t[n-1]].key
  若要求最終結果是:
       R[0].key≤R[1].key≤…≤R[n-1].key
則可以在排序結束後,再按輔助表所規定的次序重排各記錄,完成這種重排的時間是O(n)。

一、冒泡排序(BubbleSort)


1. 基本思想:
  兩兩比較待排序數據元素的大小,發現兩個數據元素的次序相反時即進行交換,直到沒有反序的數據元素爲止。
2. 排序過程:
  設想被排序的數組R[1..N]垂直豎立,將每個數據元素看作有重量的氣泡,根據輕氣泡不能在重氣泡之下的原則,從下往上掃描數組R,凡掃描到違反本原則的輕氣泡,就使其向上"漂浮",如此反覆進行,直至最後任何兩個氣泡都是輕者在上,重者在下爲止。
【示例】:
49 13 13 13 13 13 13 13 
38 49 27 27 27 27 27 27
65 38 49 38 38 38 38 38
97 65 38 49 49 49 49 49
76 97 65 49 49 49 49 49
13 76 97 65 65 65 65 65
27 27 76 97 76 76 76 76
49 49 49 76 97 97 97 97

java代碼實現:

/**  
02.* 冒泡排序:執行完一次內for循環後,最小的一個數放到了數組的最前面(跟那一個排序算法* 不一樣)。相鄰位置之間交換  
03.*/    
04.    
05.public class BubbleSort {       
06.      
07.    /**    
08.     * 排序算法的實現,對數組中指定的元素進行排序    
09.     * @param array 待排序的數組    
10.     * @param from 從哪裏開始排序    
11.     * @param end 排到哪裏    
12.     * @param c 比較器    
13.     */      
14.    public void bubble(Integer[] array, int from, int end) {       
15.        //需array.length - 1輪比較       
16.        for (int k = 1; k < end - from + 1; k++) {       
17.            //每輪循環中從最後一個元素開始向前起泡,直到i=k止,即i等於輪次止       
18.            for (int i = end - from; i >= k; i--) {       
19.                //按照一種規則(後面元素不能小於前面元素)排序       
20.                if ((array[i].compareTo(array[i - 1])) < 0) {       
21.                    //如果後面元素小於了(當然是大於還是小於要看比較器實現了)前面的元素,則前後交換       
22.                    swap(array, i, i - 1);       
23.                }       
24.            }       
25.        }       
26.    }       
27.        
28.    /**    
29.     * 交換數組中的兩個元素的位置    
30.     * @param array 待交換的數組    
31.     * @param i 第一個元素    
32.     * @param j 第二個元素    
33.     */      
34.    public void swap(Integer[] array, int i, int j) {       
35.        if (i != j) {//只有不是同一位置時才需交換       
36.            Integer tmp = array[i];       
37.            array[i] = array[j];       
38.            array[j] = tmp;       
39.        }       
40.    }       
41.    
42.        
43.    /**     
44.    * 測試     
45.    * @param args     
46.    */      
47.    public static void main(String[] args) {       
48.        Integer[] intgArr = { 7, 2, 4, 3, 12, 1, 9, 6, 8, 5, 11, 10 };       
49.        BubbleSort bubblesort = new BubbleSort();       
50.        bubblesort.bubble(intgArr,0,intgArr.length-1);    
51.        for(Integer intObj:intgArr){    
52.            System.out.print(intObj + " ");    
53.        }    
54.    }       
55.}      
  1.  

另外一種實現方式:

/** 
冒泡排序:執行完一次內for循環後,最大的一個數放到了數組的最後面。相鄰位置之間交換 
*/  
public class BubbleSort2{  
    public static void main(String[] args){  
        int[] a = {3,5,9,4,7,8,6,1,2};  
        BubbleSort2 bubble = new BubbleSort2();  
        bubble.bubble(a);  
        for(int num:a){  
            System.out.print(num + " ");  
        }  
    }  
      
    public void bubble(int[] a){  
        for(int i=a.length-1;i>0;i--){  
            for(int j=0;j<i;j++){  
                if(new Integer(a[j]).compareTo(new Integer(a[j+1]))>0){  
                    swap(a,j,j+1);  
                }  
            }  
        }  
    }  
      
    public void swap(int[] a,int x,int y){  
        int temp;  
        temp=a[x];  
        a[x]=a[y];  
        a[y]=temp;  
    }  
}  

 

二、選擇排序


1. 基本思想:
  每一趟從待排序的數據元素中選出最小(或最大)的一個元素,順序放在已排好序的數列的最後,直到全部待排序的數據元素排完。
2. 排序過程:
【示例】:
  初始關鍵字 [49 38 65 97 76 13 27 49]
第一趟排序後 13 [38 65 97 76 49 27 49]
第二趟排序後 13 27 [65 97 76 49 38 49]
第三趟排序後 13 27 38 [97 76 49 65 49]
第四趟排序後 13 27 38 49 [49 97 65 76]
第五趟排序後 13 27 38 49 49 [97 97 76]
第六趟排序後 13 27 38 49 49 76 [76 97]
第七趟排序後 13 27 38 49 49 76 76 [ 97]
最後排序結果 13 27 38 49 49 76 76 97

java代碼實現:
 

01./**  

02.* 簡單選擇排序:執行完一次內for循環後最小的一個數放在了數組的最前面。  

03.*/    

04.public class SelectSort {       

05.      

06.    /**    

07.     * 排序算法的實現,對數組中指定的元素進行排序    

08.     * @param array 待排序的數組    

09.     * @param from 從哪裏開始排序    

10.     * @param end 排到哪裏    

11.     * @param c 比較器    

12.     */      

13.    public void select(Integer[] array) {       

14.        int minIndex;//最小索引       

15.        /*    

16.         * 循環整個數組(其實這裏的上界爲 array.length - 1 即可,因爲當 i= array.length-1    

17.         * 時,最後一個元素就已是最大的了,如果爲array.length時,內層循環將不再循環),每輪假設    

18.         * 第一個元素爲最小元素,如果從第一元素後能選出比第一個元素更小元素,則讓讓最小元素與第一    

19.         * 個元素交換     

20.         */      

21.        for (int i=0; i<array.length; i++) {       

22.            minIndex = i;//假設每輪第一個元素爲最小元素       

23.            //從假設的最小元素的下一元素開始循環       

24.            for (int j=i+1;j<array.length; j++) {       

25.                //如果發現有比當前array[smallIndex]更小元素,則記下該元素的索引於smallIndex中       

26.                if ((array[j].compareTo(array[minIndex])) < 0) {       

27.                    minIndex = j;       

28.                }       

29.            }       

30.      

31.            //先前只是記錄最小元素索引,當最小元素索引確定後,再與每輪的第一個元素交換       

32.            swap(array, i, minIndex);       

33.        }       

34.    }       

35.        

36.    public static void swap(Integer[] intgArr,int x,int y){    

37.        //Integer temp; //這個也行    

38.        int temp;    

39.        temp=intgArr[x];    

40.        intgArr[x]=intgArr[y];    

41.        intgArr[y]=temp;    

42.    }    

43.        

44.    /**    

45.     * 測試    

46.     * @param args    

47.     */      

48.    public static void main(String[] args) {       

49.        Integer[] intgArr = { 5, 9, 1, 4, 2, 6, 3, 8, 0, 7 };       

50.        SelectSort insertSort = new SelectSort();    

51.        insertSort.select(intgArr);    

52.        for(Integer intObj:intgArr){    

53.            System.out.print(intObj + " ");    

54.        }      

55.            

56.    }       

57.}       

   

三、插入排序(Insertion Sort)


1. 基本思想:
  每次將一個待排序的數據元素,插入到前面已經排好序的數列中的適當位置,使數列依然有序;直到待排序數據元素全部插入完爲止。
2. 排序過程:  
【示例】:
[初始關鍵字] [49] 38 65 97 76 13 27 49
    J=2(38) [38 49] 65 97 76 13 27 49
    J=3(65) [38 49 65] 97 76 13 27 49
    J=4(97) [38 49 65 97] 76 13 27 49
    J=5(76) [38 49 65 76 97] 13 27 49
    J=6(13) [13 38 49 65 76 97] 27 49
    J=7(27) [13 27 38 49 65 76 97] 49
    J=8(49) [13 27 38 49 49 65 76 97] 

java代碼實現:

  • /**
    * 直接插入排序:
    * 注意所有排序都是從小到大排。
    */ 
    
    public class InsertSort {    
       
        /** 
         * 排序算法的實現,對數組中指定的元素進行排序 
         * @param array 待排序的數組 
         * @param from 從哪裏開始排序 
         * @param end 排到哪裏 
         * @param c 比較器 
         */   
        public void insert(Integer[] array,int from, int end) {    
       
            /* 
             * 第一層循環:對待插入(排序)的元素進行循環 
             * 從待排序數組斷的第二個元素開始循環,到最後一個元素(包括)止 
             */   
            for (int i=from+1; i<=end; i++) {    
                /* 
                 * 第二層循環:對有序數組進行循環,且從有序數組最第一個元素開始向後循環 
                 * 找到第一個大於待插入的元素 
                 * 有序數組初始元素只有一個,且爲源數組的第一個元素,一個元素數組總是有序的 
                 */   
                for (int j =0; j < i; j++) {    
                    Integer insertedElem = array[i];//待插入到有序數組的元素   
                    //從有序數組中最一個元素開始查找第一個大於待插入的元素   
                    if ((array[j].compareTo(insertedElem)) >0) {    
                        //找到插入點後,從插入點開始向後所有元素後移一位   
                        move(array, j, i - 1);    
                        //將待排序元素插入到有序數組中   
                        array[j] = insertedElem;    
                        break;    
                    }    
                }    
            } 
             
            //=======以下是java.util.Arrays的插入排序算法的實現    
            /* 
             * 該算法看起來比較簡潔一j點,有點像冒泡算法。 
    * 將數組邏輯上分成前後兩個集合,前面的集合是已經排序好序的元素,而後面集合爲待排序的
             * 集合,每次內層循從後面集合中拿出一個元素,通過冒泡的形式,從前面集合最後一個元素開 
             * 始往前比較,如果發現前面元素大於後面元素,則交換,否則循環退出 
             *  
             * 總感覺這種算術有點怪怪,既然是插入排序,應該是先找到插入點,而後再將待排序的元素插 
             * 入到的插入點上,那麼其他元素就必然向後移,感覺算法與排序名稱不匹,但返過來與上面實 
             * 現比,其實是一樣的,只是上面先找插入點,待找到後一次性將大的元素向後移,而該算法卻 
             * 是走一步看一步,一步一步將待排序元素往前移 
             */   
            /* 
            for (int i = from; i <= end; i++) { 
                for (int j = i; j > from && c.compare(array[j - 1], array[j]) > 0; j--) { 
                    swap(array, j, j - 1); 
                } 
            } 
            */   
        }    
         
         
         /** 
         * 數組元素後移 
         * @param array 待移動的數組 
         * @param startIndex 從哪個開始移 
         * @param endIndex 到哪個元素止 
         */   
        public void move(Integer[] array,int startIndex, int endIndex) {    
            for (int i = endIndex; i >= startIndex; i--) {    
                array[i+1] = array[i];    
            }    
        }    
    
         
        /** 
         * 測試 
         * @param args 
         */   
        public staticvoid main(String[] args) {    
            Integer[] intgArr = { 5, 9, 1, 4, 2, 6, 3, 8, 0, 7 };    
            InsertSort insertSort = new InsertSort();    
            insertSort.insert(intgArr,0,intgArr.length-1); 
            for(Integer intObj:intgArr){ 
                System.out.print(intObj + " "); 
            } 
        }    
    }   

     

  • 四、希爾排序

    java代碼實現:

    01./**  
    02.* 插入排序----希爾排序:我們選擇步長爲:15,7,3,1  
    03.* 我們選擇步長公式爲:2^k-1,2^(k-1)-1,……,15,7,3,1     (2^4-1,2^3-1,2^2-1,2^1-1)  
    04.* 注意所有排序都是從小到大排。  
    05.*/    
    06.public class ShellSort {       
    07.      
    08.    /**    
    09.     * 排序算法的實現,對數組中指定的元素進行排序    
    10.     * @param array 待排序的數組    
    11.     * @param from 從哪裏開始排序    
    12.     * @param end 排到哪裏    
    13.     * @param c 比較器    
    14.     */      
    15.    public void sort(Integer[] array, int from, int end) {       
    16.        //初始步長,實質爲每輪的分組數       
    17.        int step = initialStep(end - from + 1);       
    18.      
    19.        //第一層循環是對排序輪次進行循環。(step + 1) / 2 - 1 爲下一輪步長值       
    20.        for (; step >= 1; step = (step + 1) / 2 - 1) {       
    21.            //對每輪裏的每個分組進行循環       
    22.            for (int groupIndex = 0; groupIndex < step; groupIndex++) {       
    23.      
    24.                //對每組進行直接插入排序       
    25.                insertSort(array, groupIndex, step, end);       
    26.            }       
    27.        }       
    28.    }       
    29.      
    30.    /**    
    31.     * 直接插入排序實現    
    32.     * @param array 待排序數組    
    33.     * @param groupIndex 對每輪的哪一組進行排序    
    34.     * @param step 步長    
    35.     * @param end 整個數組要排哪個元素止    
    36.     * @param c 比較器    
    37.     */      
    38.    public void insertSort(Integer[] array, int groupIndex, int step, int end) {       
    39.        int startIndex = groupIndex;//從哪裏開始排序       
    40.        int endIndex = startIndex;//排到哪裏       
    41.        /*    
    42.         * 排到哪裏需要計算得到,從開始排序元素開始,以step步長,可求得下元素是否在數組範圍內,    
    43.         * 如果在數組範圍內,則繼續循環,直到索引超現數組範圍    
    44.         */      
    45.        while ((endIndex + step) <= end) {       
    46.            endIndex += step;       
    47.        }       
    48.      
    49.        // i爲每小組裏的第二個元素開始       
    50.        for (int i = groupIndex + step; i <= end; i += step) {       
    51.            for (int j = groupIndex; j < i; j += step) {       
    52.                Integer insertedElem = array[i];       
    53.                //從有序數組中最一個元素開始查找第一個大於待插入的元素       
    54.                if ((array[j].compareTo(insertedElem)) >= 0) {       
    55.                    //找到插入點後,從插入點開始向後所有元素後移一位       
    56.                    move(array, j, i - step, step);       
    57.                    array[j] = insertedElem;       
    58.                    break;       
    59.                }       
    60.            }       
    61.        }       
    62.    }       
    63.      
    64.    /**    
    65.     * 根據數組長度求初始步長    
    66.     *     
    67.     * 我們選擇步長的公式爲:2^k-1,2^(k-1)-1,...,15,7,3,1 ,其中2^k 減一即爲該步長序列,k    
    68.     * 爲排序輪次    
    69.     *     
    70.     * 初始步長:step = 2^k-1     
    71.     * 初始步長約束條件:step < len - 1 初始步長的值要小於數組長度還要減一的值(因    
    72.     * 爲第一輪分組時儘量不要分爲一組,除非數組本身的長度就小於等於4)    
    73.     *     
    74.     * 由上面兩個關係試可以得知:2^k - 1 < len - 1 關係式,其中k爲輪次,如果把 2^k 表 達式    
    75.     * 轉換成 step 表達式,則 2^k-1 可使用 (step + 1)*2-1 替換(因爲 step+1 相當於第k-1    
    76.     * 輪的步長,所以在 step+1 基礎上乘以 2 就相當於 2^k 了),即步長與數組長度的關係不等式爲    
    77.     * (step + 1)*2 - 1 < len -1    
    78.     *     
    79.     * @param len 數組長度    
    80.     * @return    
    81.     */      
    82.    public static int initialStep(int len) {       
    83.        /*    
    84.         * 初始值設置爲步長公式中的最小步長,從最小步長推導出最長初始步長值,即按照以下公式來推:    
    85.         * 1,3,7,15,...,2^(k-1)-1,2^k-1    
    86.         * 如果數組長度小於等於4時,步長爲1,即長度小於等於4的數組不用分組,此時直接退化爲直接插入排序    
    87.         */      
    88.        int step = 1;       
    89.      
    90.        //試探下一個步長是否滿足條件,如果滿足條件,則步長置爲下一步長       
    91.        while ((step + 1) * 2 - 1 < len - 1) {       
    92.            step = (step + 1) * 2 - 1;       
    93.        }       
    94.      
    95.        System.out.println("初始步長 : " + step);       
    96.        return step;       
    97.    }       
    98.        
    99.    /**    
    100.     * 以指定的步長將數組元素後移,步長指定每個元素間的間隔    
    101.     * @param array 待排序數組    
    102.     * @param startIndex 從哪裏開始移    
    103.     * @param endIndex 到哪個元素止    
    104.     * @param step 步長    
    105.     */      
    106.    protected final void move(Integer[] array, int startIndex, int endIndex, int step)  {       
    107.        for (int i = endIndex; i >= startIndex; i -= step) {       
    108.            array[i + step] = array[i];       
    109.        }       
    110.    }       
    111.    
    112.        
    113.    /**    
    114.     * 測試    
    115.     * @param args    
    116.     */      
    117.    public static void main(String[] args) {       
    118.        Integer[] intgArr = { 5, 9, 1, 4, 8, 2, 6, 3, 7, 0 };       
    119.        ShellSort shellSort = new ShellSort();       
    120.        shellSort.sort(intgArr,0,intgArr.length-1);    
    121.        for(Integer intObj:intgArr){    
    122.            System.out.print(intObj + " ");    
    123.        }    
    124.    }       
    125.}       
    

     

    五、快速排序(Quick Sort)


  • 1. 基本思想:
      在當前無序區R[1..H]中任取一個數據元素作爲比較的"基準"(不妨記爲X),用此基準將當前無序區劃分爲左右兩個較小的無序區:R[1..I-1]和R[I+1..H],且左邊的無序子區中數據元素均小於等於基準元素,右邊的無序子區中數據元素均大於等於基準元素,而基準X則位於最終排序的位置上,即R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),當R[1..I-1]和R[I+1..H]均非空時,分別對它們進行上述的劃分過程,直至所有無序子區中的數據元素均已排序爲止。
    2. 排序過程:
    【示例】:

    初始關鍵字 [49 38 65 97 76 13 27 49]
    一趟排序之後 [27 38 13] 49 [76 97 65 49] 
    二趟排序之後 [13] 27 [38] 49 [49 65]76 [97]
    三趟排序之後 13 27 38 49 49 [65]76 97
    最後的排序結果 13 27 38 49 49 65 76 97 
    各趟排序之後的狀態

    java代碼實現:

    01./**  
    02.* 快速排序:  
    03.*/    
    04.    
    05.public class QuickSort {       
    06.      
    07.    /**    
    08.     * 排序算法的實現,對數組中指定的元素進行排序    
    09.     * @param array 待排序的數組    
    10.     * @param from 從哪裏開始排序    
    11.     * @param end 排到哪裏    
    12.     * @param c 比較器    
    13.     */      
    14.    //定義了一個這樣的公有方法sort,在這個方法體裏面執行私有方法quckSort(這種方式值得借鑑)。    
    15.    public void sort(Integer[] array, int from, int end) {       
    16.        quickSort(array, from, end);       
    17.    }       
    18.      
    19.    /**    
    20.     * 遞歸快速排序實現    
    21.     * @param array 待排序數組    
    22.     * @param low 低指針    
    23.     * @param high 高指針    
    24.     * @param c 比較器    
    25.     */      
    26.    private void quickSort(Integer[] array, int low, int high) {       
    27.        /*    
    28.         * 如果分區中的低指針小於高指針時循環;如果low=higth說明數組只有一個元素,無需再處理;    
    29.         * 如果low > higth,則說明上次樞紐元素的位置pivot就是low或者是higth,此種情況    
    30.         * 下分區不存,也不需處理    
    31.         */      
    32.        if (low < high) {       
    33.            //對分區進行排序整理       
    34.                
    35.            //int pivot = partition1(array, low, high);    
    36.            int pivot = partition2(array, low, high);       
    37.            //int pivot = partition3(array, low, high);          
    38.                
    39.            /*    
    40.             * 以pivot爲邊界,把數組分成三部分[low, pivot - 1]、[pivot]、[pivot + 1, high]    
    41.             * 其中[pivot]爲樞紐元素,不需處理,再對[low, pivot - 1]與[pivot + 1, high]    
    42.             * 各自進行分區排序整理與進一步分區    
    43.             */      
    44.            quickSort(array, low, pivot - 1);       
    45.            quickSort(array, pivot + 1, high);       
    46.        }       
    47.      
    48.    }       
    49.      
    50.    /**    
    51.     * 實現一    
    52.     *     
    53.     * @param array 待排序數組    
    54.     * @param low 低指針    
    55.     * @param high 高指針    
    56.     * @param c 比較器    
    57.     * @return int 調整後中樞位置    
    58.     */      
    59.    private int partition1(Integer[] array, int low, int high) {       
    60.        Integer pivotElem = array[low];//以第一個元素爲中樞元素       
    61.        //從前向後依次指向比中樞元素小的元素,剛開始時指向中樞,也是小於與大小中樞的元素的分界點       
    62.        int border = low;       
    63.      
    64.        /*    
    65.         * 在中樞元素後面的元素中查找小於中樞元素的所有元素,並依次從第二個位置從前往後存放    
    66.         * 注,這裏最好使用i來移動,如果直接移動low的話,最後不知道數組的邊界了,但後面需要    
    67.         * 知道數組的邊界    
    68.         */      
    69.        for (int i = low + 1; i <= high; i++) {       
    70.            //如果找到一個比中樞元素小的元素       
    71.            if ((array[i].compareTo(pivotElem)) < 0) {       
    72.                swap(array, ++border, i);//border前移,表示有小於中樞元素的元素       
    73.            }       
    74.        }       
    75.        /*    
    76.         * 如果border沒有移動時說明說明後面的元素都比中樞元素要大,border與low相等,此時是    
    77.         * 同一位置交換,是否交換都沒關係;當border移到了high時說明所有元素都小於中樞元素,此    
    78.         * 時將中樞元素與最後一個元素交換即可,即low與high進行交換,大的中樞元素移到了 序列最    
    79.         * 後;如果 low <minIndex< high,表 明中樞後面的元素前部分小於中樞元素,而後部分大於    
    80.         * 中樞元素,此時中樞元素與前部分數組中最後一個小於它的元素交換位置,使得中樞元素放置在    
    81.         * 正確的位置    
    82.         */      
    83.        swap(array, border, low);       
    84.        return border;       
    85.    }       
    86.      
    87.    /**    
    88.     * 實現二    
    89.     *     
    90.     * @param array 待排序數組    
    91.     * @param low 待排序區低指針    
    92.     * @param high 待排序區高指針    
    93.     * @param c 比較器    
    94.     * @return int 調整後中樞位置    
    95.     */      
    96.    private int partition2(Integer[] array, int low, int high) {       
    97.        int pivot = low;//中樞元素位置,我們以第一個元素爲中樞元素       
    98.        //退出條件這裏只可能是 low = high       
    99.        while (true) {       
    100.            if (pivot != high) {//如果中樞元素在低指針位置時,我們移動高指針       
    101.                //如果高指針元素小於中樞元素時,則與中樞元素交換       
    102.                if ((array[high].compareTo(array[pivot])) < 0) {       
    103.                    swap(array, high, pivot);       
    104.                    //交換後中樞元素在高指針位置了       
    105.                    pivot = high;       
    106.                } else {//如果未找到小於中樞元素,則高指針前移繼續找       
    107.                    high--;       
    108.                }       
    109.            } else {//否則中樞元素在高指針位置       
    110.                //如果低指針元素大於中樞元素時,則與中樞元素交換       
    111.                if ((array[low].compareTo(array[pivot])) > 0) {       
    112.                    swap(array, low, pivot);       
    113.                    //交換後中樞元素在低指針位置了       
    114.                    pivot = low;       
    115.                } else {//如果未找到大於中樞元素,則低指針後移繼續找       
    116.                    low++;       
    117.                }       
    118.            }       
    119.            if (low == high) {       
    120.                break;       
    121.            }       
    122.        }       
    123.        //返回中樞元素所在位置,以便下次分區       
    124.        return pivot;       
    125.    }       
    126.      
    127.    /**    
    128.     * 實現三    
    129.     *     
    130.     * @param array 待排序數組    
    131.     * @param low 待排序區低指針    
    132.     * @param high 待排序區高指針    
    133.     * @param c 比較器    
    134.     * @return int 調整後中樞位置    
    135.     */      
    136.    private int partition3(Integer[] array, int low, int high) {       
    137.        int pivot = low;//中樞元素位置,我們以第一個元素爲中樞元素       
    138.        low++;       
    139.        //----調整高低指針所指向的元素順序,把小於中樞元素的移到前部分,大於中樞元素的移到後面部分       
    140.        //退出條件這裏只可能是 low = high       
    141.      
    142.        while (true) {       
    143.            //如果高指針未超出低指針       
    144.            while (low < high) {       
    145.                //如果低指針指向的元素大於或等於中樞元素時表示找到了,退出,注:等於時也要後移       
    146.                if ((array[low].compareTo(array[pivot])) >= 0) {       
    147.                    break;       
    148.                } else {//如果低指針指向的元素小於中樞元素時繼續找       
    149.                    low++;       
    150.                }       
    151.            }       
    152.      
    153.            while (high > low) {       
    154.                //如果高指針指向的元素小於中樞元素時表示找到,退出       
    155.                if ((array[high].compareTo(array[pivot])) < 0) {       
    156.                    break;       
    157.                } else {//如果高指針指向的元素大於中樞元素時繼續找       
    158.                    high--;       
    159.                }       
    160.            }       
    161.            //退出上面循環時 low = high       
    162.            if (low == high) {       
    163.                break;       
    164.            }       
    165.      
    166.            swap(array, low, high);       
    167.        }       
    168.      
    169.        //----高低指針所指向的元素排序完成後,還得要把中樞元素放到適當的位置       
    170.        if ((array[pivot].compareTo(array[low])) > 0) {       
    171.            //如果退出循環時中樞元素大於了低指針或高指針元素時,中樞元素需與low元素交換       
    172.            swap(array, low, pivot);       
    173.            pivot = low;       
    174.        } else if ((array[pivot].compareTo(array[low])) <= 0) {       
    175.            swap(array, low - 1, pivot);       
    176.            pivot = low - 1;       
    177.        }       
    178.      
    179.        //返回中樞元素所在位置,以便下次分區       
    180.        return pivot;       
    181.    }       
    182.        
    183.        /**    
    184.     * 交換數組中的兩個元素的位置    
    185.     * @param array 待交換的數組    
    186.     * @param i 第一個元素    
    187.     * @param j 第二個元素    
    188.     */      
    189.    public void swap(Integer[] array, int i, int j) {       
    190.        if (i != j) {//只有不是同一位置時才需交換       
    191.            Integer tmp = array[i];       
    192.            array[i] = array[j];       
    193.            array[j] = tmp;       
    194.        }       
    195.    }     
    196.        
    197.    /**     
    198.    * 測試     
    199.    * @param args     
    200.    */      
    201.    public static void main(String[] args) {       
    202.        Integer[] intgArr = { 5, 9, 1, 4, 2, 6, 3, 8, 0, 7 };      
    203.        QuickSort quicksort = new QuickSort();       
    204.        quicksort.sort(intgArr,0,intgArr.length-1);    
    205.        for(Integer intObj:intgArr){    
    206.            System.out.print(intObj + " ");    
    207.        }    
    208.    }       
    209.}       
    1.  

     

    六、歸併排序

    java代碼實現:

    01./**   
    02.歸併排序:裏面是一個遞歸程序,深刻理解之。   
    03.*/    
    04.public class MergeSort{       
    05.      
    06.    /**    
    07.     * 遞歸劃分數組    
    08.     * @param arr    
    09.     * @param from    
    10.     * @param end    
    11.     * @param c void    
    12.     */      
    13.    public void partition(Integer[] arr, int from, int end) {       
    14.        //劃分到數組只有一個元素時纔不進行再劃分       
    15.        if (from < end) {       
    16.            //從中間劃分成兩個數組       
    17.            int mid = (from + end) / 2;       
    18.            partition(arr, from, mid);       
    19.            partition(arr, mid + 1, end);       
    20.            //合併劃分後的兩個數組       
    21.            merge(arr, from, end, mid);       
    22.        }       
    23.    }       
    24.      
    25.    /**    
    26.     * 數組合並,合併過程中對兩部分數組進行排序    
    27.     * 前後兩部分數組裏是有序的    
    28.     * @param arr    
    29.     * @param from    
    30.     * @param end    
    31.     * @param mid    
    32.     * @param c void    
    33.     */      
    34.    public void merge(Integer[] arr, int from, int end, int mid) {       
    35.        Integer[] tmpArr = new Integer[10];    
    36.        int tmpArrIndex = 0;//指向臨時數組       
    37.        int part1ArrIndex = from;//指向第一部分數組       
    38.        int part2ArrIndex = mid + 1;//指向第二部分數組       
    39.      
    40.        //由於兩部分數組裏是有序的,所以每部分可以從第一個元素依次取到最後一個元素,再對兩部分       
    41.        //取出的元素進行比較。只要某部分數組元素取完後,退出循環       
    42.        while ((part1ArrIndex <= mid) && (part2ArrIndex <= end)) {       
    43.            //從兩部分數組裏各取一個進行比較,取最小一個並放入臨時數組中       
    44.            if (arr[part1ArrIndex] - arr[part2ArrIndex] < 0) {       
    45.                //如果第一部分數組元素小,則將第一部分數組元素放入臨時數組中,並且臨時數組指針       
    46.                //tmpArrIndex下移一個以做好下次存儲位置準備,前部分數組指針part1ArrIndex       
    47.                //也要下移一個以便下次取出下一個元素與後部分數組元素比較       
    48.                tmpArr[tmpArrIndex++] = arr[part1ArrIndex++];       
    49.            } else {       
    50.                //如果第二部分數組元素小,則將第二部分數組元素放入臨時數組中       
    51.                tmpArr[tmpArrIndex++] = arr[part2ArrIndex++];       
    52.            }       
    53.        }       
    54.        //由於退出循環後,兩部分數組中可能有一個數組元素還未處理完,所以需要額外的處理,當然不可       
    55.        //能兩部分數組都有未處理完的元素,所以下面兩個循環最多隻有一個會執行,並且都是大於已放入       
    56.        //臨時數組中的元素       
    57.        while (part1ArrIndex <= mid) {       
    58.            tmpArr[tmpArrIndex++] = arr[part1ArrIndex++];       
    59.        }       
    60.        while (part2ArrIndex <= end) {       
    61.            tmpArr[tmpArrIndex++] = arr[part2ArrIndex++];       
    62.        }       
    63.      
    64.        //最後把臨時數組拷貝到源數組相同的位置       
    65.        System.arraycopy(tmpArr, 0, arr, from, end - from + 1);       
    66.    }       
    67.      
    68.    /**    
    69.     * 測試    
    70.     * @param args    
    71.     */      
    72.    public static void main(String[] args) {       
    73.        Integer[] intgArr = {5,9,1,4,2,6,3,8,0,7};       
    74.        MergeSort insertSort = new MergeSort();       
    75.        insertSort.partition(intgArr,0,intgArr.length-1);    
    76.        for(Integer a:intgArr){    
    77.            System.out.print(a + " ");    
    78.        }    
    79.    }       
    80.}       
    1.  


    七、堆排序(Heap Sort)


  • 1. 基本思想:
      堆排序是一樹形選擇排序,在排序過程中,將R[1..N]看成是一顆完全二叉樹的順序存儲結構,利用完全二叉樹中雙親結點和孩子結點之間的內在關係來選擇最小的元素。
    2. 堆的定義: N個元素的序列K1,K2,K3,...,Kn.稱爲堆,當且僅當該序列滿足特性:
           Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2])

      堆實質上是滿足如下性質的完全二叉樹:樹中任一非葉子結點的關鍵字均大於等於其孩子結點的關鍵字。例如序列10,15,56,25,30,70就是一個堆,它對應的完全二叉樹如上圖所示。這種堆中根結點(稱爲堆頂)的關鍵字最小,我們把它稱爲小根堆。反之,若完全二叉樹中任一非葉子結點的關鍵字均大於等於其孩子的關鍵字,則稱之爲大根堆。
    3. 排序過程:
    堆排序正是利用小根堆(或大根堆)來選取當前無序區中關鍵字小(或最大)的記錄實現排序的。我們不妨利用大根堆來排序。每一趟排序的基本操作是:將當前無序區調整爲一個大根堆,選取關鍵字最大的堆頂記錄,將它和無序區中的最後一個記錄交換。這樣,正好和直接選擇排序相反,有序區是在原記錄區的尾部形成並逐步向前擴大到整個記錄區。
    【示例】:對關鍵字序列42,13,91,23,24,16,05,88建堆 

    java代碼實現:

    01./**  
    02.* 選擇排序之堆排序:  
    03.*/    
    04.public class HeapSort {       
    05.      
    06.    /**    
    07.     * 排序算法的實現,對數組中指定的元素進行排序    
    08.     * @param array 待排序的數組    
    09.     * @param from 從哪裏開始排序    
    10.     * @param end 排到哪裏    
    11.     * @param c 比較器    
    12.     */      
    13.    public void sort(Integer[] array, int from, int end) {       
    14.        //創建初始堆       
    15.        initialHeap(array, from, end);       
    16.      
    17.        /*    
    18.         * 對初始堆進行循環,且從最後一個節點開始,直接樹只有兩個節點止    
    19.         * 每輪循環後丟棄最後一個葉子節點,再看作一個新的樹    
    20.         */      
    21.        for (int i = end - from + 1; i >= 2; i--) {       
    22.            //根節點與最後一個葉子節點交換位置,即數組中的第一個元素與最後一個元素互換       
    23.            swap(array, from, i - 1);       
    24.            //交換後需要重新調整堆       
    25.            adjustNote(array, 1, i - 1);       
    26.        }       
    27.      
    28.    }       
    29.      
    30.    /**    
    31.     * 初始化堆    
    32.     * 比如原序列爲:7,2,4,3,12,1,9,6,8,5,10,11    
    33.     * 則初始堆爲:1,2,4,3,5,7,9,6,8,12,10,11    
    34.     * @param arr 排序數組    
    35.     * @param from 從哪    
    36.     * @param end 到哪    
    37.     * @param c 比較器    
    38.     */      
    39.    private void initialHeap(Integer[] arr, int from, int end) {       
    40.        int lastBranchIndex = (end - from + 1) / 2;//最後一個非葉子節點       
    41.        //對所有的非葉子節點進行循環 ,且從最一個非葉子節點開始       
    42.        for (int i = lastBranchIndex; i >= 1; i--) {       
    43.            adjustNote(arr, i, end - from + 1);       
    44.        }       
    45.    }       
    46.      
    47.    /**    
    48.     * 調整節點順序,從父、左右子節點三個節點中選擇一個最大節點與父節點轉換    
    49.     * @param arr 待排序數組    
    50.     * @param parentNodeIndex 要調整的節點,與它的子節點一起進行調整    
    51.     * @param len 樹的節點數    
    52.     * @param c 比較器    
    53.     */      
    54.    private void adjustNote(Integer[] arr, int parentNodeIndex, int len) {       
    55.        int minNodeIndex = parentNodeIndex;       
    56.        //如果有左子樹,i * 2爲左子節點索引        
    57.        if (parentNodeIndex * 2 <= len) {       
    58.            //如果父節點小於左子樹時        
    59.            if ((arr[parentNodeIndex - 1].compareTo(arr[parentNodeIndex * 2 - 1])) < 0) {       
    60.                minNodeIndex = parentNodeIndex * 2;//記錄最大索引爲左子節點索引        
    61.            }       
    62.      
    63.            // 只有在有或子樹的前提下才可能有右子樹,再進一步斷判是否有右子樹        
    64.            if (parentNodeIndex * 2 + 1 <= len) {       
    65.                //如果右子樹比最大節點更大        
    66.                if ((arr[minNodeIndex - 1].compareTo(arr[(parentNodeIndex * 2 + 1) - 1])) < 0) {       
    67.                    minNodeIndex = parentNodeIndex * 2 + 1;//記錄最大索引爲右子節點索引        
    68.                }       
    69.            }       
    70.        }       
    71.      
    72.        //如果在父節點、左、右子節點三都中,最大節點不是父節點時需交換,把最大的與父節點交換,創建大頂堆       
    73.        if (minNodeIndex != parentNodeIndex) {       
    74.            swap(arr, parentNodeIndex - 1, minNodeIndex - 1);       
    75.            //交換後可能需要重建堆,原父節點可能需要繼續下沉       
    76.            if (minNodeIndex * 2 <= len) {//是否有子節點,注,只需判斷是否有左子樹即可知道       
    77.                adjustNote(arr, minNodeIndex, len);       
    78.            }       
    79.        }       
    80.    }       
    81.        
    82.            /**    
    83.     * 交換數組中的兩個元素的位置    
    84.     * @param array 待交換的數組    
    85.     * @param i 第一個元素    
    86.     * @param j 第二個元素    
    87.     */      
    88.    public void swap(Integer[] array, int i, int j) {       
    89.        if (i != j) {//只有不是同一位置時才需交換       
    90.            Integer tmp = array[i];       
    91.            array[i] = array[j];       
    92.            array[j] = tmp;       
    93.        }       
    94.    }     
    95.        
    96.    /**     
    97.    * 測試     
    98.    * @param args     
    99.    */      
    100.    public static void main(String[] args) {       
    101.        Integer[] intgArr = { 5, 9, 1, 4, 2, 6, 3, 8, 0, 7 };      
    102.        HeapSort heapsort = new HeapSort();       
    103.        heapsort.sort(intgArr,0,intgArr.length-1);    
    104.        for(Integer intObj:intgArr){    
    105.            System.out.print(intObj + " ");    
    106.        }    
    107.    }       
    108.      
    109.}      
    


     

    八、桶式排序

    java代碼實現:

    01./**    
    02. * 桶式排序:    
    03. * 桶式排序不再是基於比較的了,它和基數排序同屬於分配類的排序,    
    04. * 這類排序的特點是事先要知道待排 序列的一些特徵。    
    05. * 桶式排序事先要知道待排 序列在一個範圍內,而且這個範圍應該不是很大的。    
    06. * 比如知道待排序列在[0,M)內,那麼可以分配M個桶,第I個桶記錄I的出現情況,    
    07. * 最後根據每個桶收到的位置信息把數據輸出成有序的形式。    
    08. * 這裏我們用兩個臨時性數組,一個用於記錄位置信息,一個用於方便輸出數據成有序方式,    
    09. * 另外我們假設數據落在0到MAX,如果所給數據不是從0開始,你可以把每個數減去最小的數。    
    10. */      
    11.public class BucketSort {       
    12.     public void sort(int[] keys,int from,int len,int max)       
    13.        {       
    14.            int[] temp=new int[len];       
    15.            int[] count=new int[max];       
    16.                   
    17.                   
    18.            for(int i=0;i<len;i++)       
    19.            {       
    20.                count[keys[from+i]]++;       
    21.            }       
    22.            //calculate position info       
    23.            for(int i=1;i<max;i++)       
    24.            {       
    25.                count[i]=count[i]+count[i-1];//這意味着有多少數目小於或等於i,因此它也是position+ 1       
    26.            }       
    27.                   
    28.            System.arraycopy(keys, from, temp, 0, len);       
    29.            for(int k=len-1;k>=0;k--)//從最末到開頭保持穩定性       
    30.            {       
    31.                keys[--count[temp[k]]]=temp[k];// position +1 =count       
    32.            }       
    33.        }       
    34.        /**    
    35.         * @param args    
    36.         */      
    37.        public static void main(String[] args) {       
    38.      
    39.            int[] a={1,4,8,3,2,9,5,0,7,6,9,10,9,13,14,15,11,12,17,16};       
    40.            BucketSort bucketSort=new BucketSort();       
    41.            bucketSort.sort(a,0,a.length,20);//actually is 18, but 20 will also work       
    42.                   
    43.                   
    44.            for(int i=0;i<a.length;i++)       
    45.            {       
    46.                System.out.print(a[i]+",");       
    47.            }       
    48.      
    49.        }       
    50.      
    51.      
    52.}       
    1.  

    九、基數排序

    java代碼實現:

    01./**  
    02.* 基數排序:  
    03.*/    
    04.import java.util.Arrays;     
    05.public class RadixSort {       
    06.      
    07.    /**    
    08.     * 取數x上的第d位數字    
    09.     * @param x 數    
    10.     * @param d 第幾位,從低位到高位    
    11.     * @return    
    12.     */      
    13.    public int digit(long x, long d) {       
    14.      
    15.        long pow = 1;       
    16.        while (--d > 0) {       
    17.            pow *= 10;       
    18.        }       
    19.        return (int) (x / pow % 10);       
    20.    }       
    21.      
    22.    /**    
    23.     * 基數排序實現,以升序排序(下面程序中的位記錄器count中,從第0個元素到第9個元素依次用來    
    24.     * 記錄當前比較位是0的有多少個..是9的有多少個數,而降序時則從第0個元素到第9個元素依次用來    
    25.     * 記錄當前比較位是9的有多少個..是0的有多少個數)    
    26.     * @param arr 待排序數組    
    27.     * @param digit 數組中最大數的位數    
    28.     * @return    
    29.     */      
    30.    public long[] radixSortAsc(long[] arr) {       
    31.        //從低位往高位循環       
    32.        for (int d = 1; d <= getMax(arr); d++) {       
    33.            //臨時數組,用來存放排序過程中的數據       
    34.            long[] tmpArray = new long[arr.length];       
    35.            //位記數器,從第0個元素到第9個元素依次用來記錄當前比較位是0的有多少個..是9的有多少個數       
    36.            int[] count = new int[10];       
    37.            //開始統計0有多少個,並存儲在第0位,再統計1有多少個,並存儲在第1位..依次統計到9有多少個       
    38.            for (int i = 0; i < arr.length; i++) {       
    39.                count[digit(arr[i], d)] += 1;       
    40.            }       
    41.            /*    
    42.             * 比如某次經過上面統計後結果爲:[0, 2, 3, 3, 0, 0, 0, 0, 0, 0]則經過下面計算後 結果爲:    
    43.             * [0, 2, 5, 8, 8, 8, 8, 8, 8, 8]但實質上只有如下[0, 2, 5, 8, 0, 0, 0, 0, 0, 0]中    
    44.             * 非零數纔用到,因爲其他位不存在,它們分別表示如下:2表示比較位爲1的元素可以存放在索引爲1、0的    
    45.             * 位置,5表示比較位爲2的元素可以存放在4、3、2三個(5-2=3)位置,8表示比較位爲3的元素可以存放在    
    46.             * 7、6、5三個(8-5=3)位置    
    47.             */      
    48.            for (int i = 1; i < 10; i++) {       
    49.                count[i] += count[i - 1];       
    50.            }       
    51.      
    52.            /*    
    53.             * 注,這裏只能從數組後往前循環,因爲排序時還需保持以前的已排序好的 順序,不應該打    
    54.             * 亂原來已排好的序,如果從前往後處理,則會把原來在前面會擺到後面去,因爲在處理某個    
    55.             * 元素的位置時,位記數器是從大到到小(count[digit(arr[i], d)]--)的方式來處    
    56.             * 理的,即先存放索引大的元素,再存放索引小的元素,所以需從最後一個元素開始處理。    
    57.             * 如有這樣的一個序列[212,213,312],如果按照從第一個元素開始循環的話,經過第一輪    
    58.             * 後(個位)排序後,得到這樣一個序列[312,212,213],第一次好像沒什麼問題,但問題會    
    59.             * 從第二輪開始出現,第二輪排序後,會得到[213,212,312],這樣個位爲3的元素本應該    
    60.             * 放在最後,但經過第二輪後卻排在了前面了,所以出現了問題    
    61.             */      
    62.            for (int i = arr.length - 1; i >= 0; i--) {//只能從最後一個元素往前處理       
    63.                //for (int i = 0; i < arr.length; i++) {//不能從第一個元素開始循環       
    64.                tmpArray[count[digit(arr[i], d)] - 1] = arr[i];       
    65.                count[digit(arr[i], d)]--;       
    66.            }       
    67.      
    68.            System.arraycopy(tmpArray, 0, arr, 0, tmpArray.length);       
    69.        }       
    70.        return arr;       
    71.    }       
    72.      
    73.    /**    
    74.     * 基數排序實現,以降序排序(下面程序中的位記錄器count中,從第0個元素到第9個元素依次用來    
    75.     * 記錄當前比較位是0的有多少個..是9的有多少個數,而降序時則從第0個元素到第9個元素依次用來    
    76.     * 記錄當前比較位是9的有多少個..是0的有多少個數)    
    77.     * @param arr 待排序數組    
    78.     * @return    
    79.     */      
    80.    public long[] radixSortDesc(long[] arr) {       
    81.        for (int d = 1; d <= getMax(arr); d++) {       
    82.            long[] tmpArray = new long[arr.length];       
    83.            //位記數器,從第0個元素到第9個元素依次用來記錄當前比較位是9的有多少個..是0的有多少個數       
    84.            int[] count = new int[10];       
    85.            //開始統計0有多少個,並存儲在第9位,再統計1有多少個,並存儲在第8位..依次統計       
    86.            //到9有多少個,並存儲在第0位       
    87.            for (int i = 0; i < arr.length; i++) {       
    88.                count[9 - digit(arr[i], d)] += 1;       
    89.            }       
    90.      
    91.            for (int i = 1; i < 10; i++) {       
    92.                count[i] += count[i - 1];       
    93.            }       
    94.      
    95.            for (int i = arr.length - 1; i >= 0; i--) {       
    96.                tmpArray[count[9 - digit(arr[i], d)] - 1] = arr[i];       
    97.                count[9 - digit(arr[i], d)]--;       
    98.            }       
    99.      
    100.            System.arraycopy(tmpArray, 0, arr, 0, tmpArray.length);       
    101.        }       
    102.        return arr;       
    103.    }       
    104.      
    105.    private int getMax(long[] array) {       
    106.        int maxlIndex = 0;       
    107.        for (int j = 1; j < array.length; j++) {       
    108.            if (array[j] > array[maxlIndex]) {       
    109.                maxlIndex = j;       
    110.            }       
    111.        }       
    112.        return String.valueOf(array[maxlIndex]).length();       
    113.    }       
    114.      
    115.    public static void main(String[] args) {       
    116.        long[] ary = new long[] { 123, 321, 132, 212, 213, 312, 21, 223 };       
    117.        RadixSort rs = new RadixSort();       
    118.        System.out.println("升 - " + Arrays.toString(rs.radixSortAsc(ary)));       
    119.      
    120.        ary = new long[] { 123, 321, 132, 212, 213, 312, 21, 223 };       
    121.        System.out.println("降 - " + Arrays.toString(rs.radixSortDesc(ary)));       
    122.    }       
    123.}      
    


     

    十、幾種排序算法的比較和選擇 


  • 1. 選取排序方法需要考慮的因素:
    (1) 待排序的元素數目n;
    (2) 元素本身信息量的大小;
    (3) 關鍵字的結構及其分佈情況;
    (4) 語言工具的條件,輔助空間的大小等。
    2. 小結:
    (1) 若n較小(n <= 50),則可以採用直接插入排序或直接選擇排序。由於直接插入排序所需的記錄移動操作較直接選擇排序多,因而當記錄本身信息量較大時,用直接選擇排序較好。
    (2) 若文件的初始狀態已按關鍵字基本有序,則選用直接插入或冒泡排序爲宜。
    (3) 若n較大,則應採用時間複雜度爲O(nlog2n)的排序方法:快速排序、堆排序或歸併排序。 快速排序是目前基於比較的內部排序法中被認爲是最好的方法。
    (4) 在基於比較排序方法中,每次比較兩個關鍵字的大小之後,僅僅出現兩種可能的轉移,因此可以用一棵二叉樹來描述比較判定過程,由此可以證明:當文件的n個關鍵字隨機分佈時,任何藉助於"比較"的排序算法,至少需要O(nlog2n)的時間。
    (5) 當記錄本身信息量較大時,爲避免耗費大量時間移動記錄,可以用鏈表作爲存儲結構。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章