使用Java泛型實現快速排序(快排,Quicksort)算法

快排算法的特色

  • 實用性強。
不少實際的項目中使用了快排算法。但一般對算法都進行了調整(tuning),好比Java.util.Arrays類中的sort函數就使用了快排算法,但使用了雙參考值(Dual-Pivot Quicksort)等一些改進措施。因爲快排算法爲遞歸算法,能夠用循環代替遞歸函數調用,改進性能。
  • 不須要額外的空間。
能夠將數組中的數據直接交換位置實現排序,因此理論上不須要額外的空間。

時間複雜度

  • 平均狀況:O(nlgn)
  • 最壞狀況:O(n*n),發生在當數據已是排序狀態時

快排算法的基本原理

一、從數據中選取一個值a[i]做爲參考
二、以a[i] 爲參考,將數據分紅2部分:P一、P2,P1中的數據所有≤a[i],P2中的數據所有>a[i],數據變爲{{P1}{a[i]}{P2}}
三、將P一、P2重複上述步驟,直到各部分中只剩1個數據
四、數據完成升序排列  html

示例: java

原始數據:
    {3,9,8,5,2,1,6}
第1步:選取第1個數據:3
第2步:將數據分紅2部分,左邊≤3,右邊大於>3:
    {2,1} {3} {9,8,5,6}
第3步:將各部分重複以上步驟,直到每部分只剩1個數據:
    {2,1} => {1} {2}
    {9,8,5,6} => {8,5,6} {9}=> {5,6} {8} {9}=> {5} {6} {8} {9}
第4步:數據完成升序排列:
    {1} {2} {3} {5} {6} {8} {9}

程序中數據一般保存在數組中,以int類型的數組爲例,能夠將上面的步驟寫成一個quickSort函數原型: 算法

quickSort(int begin, int end) { 
//begin爲數組的第一個數據的索引值,end爲數組的最後一個數據的索引值+1
    //若是隻有1個數據或0個數據,則程序返回
    if( begin == end || begin == (end-1) ) return; 
    int p = in[begin];//p爲選擇的參考數據,選擇第一個數據
    int a = begin +1; //a做爲2部分數據分界線的索引值
    int b = a;//b爲待比較的數據的索引值
    for( ; b < end; b++) {//將數組中的各個數據依次與參考數據進行比較
        if( in[b] < p) { //若是該數據<參考數據則將其移動到左邊
            if(a == b){a++; continue;} //若是該數據已經在左邊則不動
            int temp = in[a];//將數據移動到左邊
            in[a] = in[b];
            in[b] = temp;
            a++;//將分界線右移
        }
    }
    in[begin] = in[a-1];//講參考值移動到2組數據中間
    in[a-1] = p;
    if( a-1 > begin){ // 若是左邊有數據則將其重複上述步驟
        quickSort(begin, a);
    } 
    if( end-1 > a ) {// 若是右邊有數據則將其重複上述步驟
        quickSort(a, end);
    } 
    return; // 若是無數據返回
}


使用泛型實現快排算法

下面設計一個QuickSort類,包含了靜態函數sort(),能夠對任意類型數組進行排序。若是爲對象類型數組,則該對象類型必須實現Comparable接口,這樣才能使用compareTo函數進行比較。 數組

使用了最基本的快排算法,沒有進行優化處理。 oracle

源代碼以下: dom


import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;

public class QuickSort {
    @SuppressWarnings("unchecked")
    //對上述快排函數原型修改,使其能夠對任意對象類型數組進行排序。這個函數爲內部使用,外部排序函數接口爲sort(),sort函數要求對象必須實現Comparable接口,能夠提供編譯時類型檢測,見後文。
    private static void quickSort(Object[] in,int begin, int end) {
        if( begin == end || begin == (end-1) ) return;
        Object p = in[begin];
        int a = begin +1;
        int b = a;
        for( ; b < end; b++) {
            //該對象類型數組必須實現Comparable接口,這樣才能使用compareTo函數進行比較
            if( ((Comparable<Object>)in[b]).compareTo(p) < 0) {
                if(a == b){a++; continue;}
                Object temp = in[a];
                in[a] = in[b];
                in[b] = temp;
                a++;
            }
        }
        in[begin] = in[a-1];
        in[a-1] = p;
        if( a-1 > begin){
            quickSort(in,begin, a);
        } 
        if( end-1 > a ) {
            quickSort(in,a, end);
        } 
        return;
    }
    
    //使用泛型,對任意對象數組排序,該對象類型數組必須實現Comparable接口
    public static <T extends Comparable<? super T>> void sort(T[] input){
        quickSort(input,0,input.length);
    }
    
    //添加對List對象進行排序的功能,參考了Java中的Java.util.Collections類的sort()函數
    public static <T extends Comparable<? super T>> void sort(List<T> list){
        Object[] t = list.toArray();//將列表轉換爲數組
        quickSort(t,0,t.length); //對數組進行排序
        //數組排序完成後再寫回到列表中
        ListIterator<T> i = list.listIterator();
        for (int j=0; j<t.length; j++) {
            i.next();
            i.set((T)t[j]);
        }
    }
    
    //因爲Java中原始數據類型(int、double、byte等)沒法使用泛型,因此只能使用函數重載機制實現對這些原始類型數組(int[]、double[]、byte[]等)的排序。這裏爲了共用同一個排序函數,利用原始類型的(AutoBoxing,UnBoxing)機制將其封裝爲對應對象類型,組成新的對象數組,排序後再解封裝,這樣的缺點是須要額外的轉換步驟、額外的空間保存封裝後的數組。另外一種方式是將排序代碼複製到各個重載函數中,官方API中的Java.util.Arrays這個類中的sort()函數就是使用這種方法,能夠從Arrays類的源代碼看出。
    public static void sort(int[] input){
        Integer[] t = new Integer[input.length];
        for(int i = 0; i < input.length; i++){
            t[i] = input[i];//封裝
        }
        quickSort(t,0,t.length);//排序
        for(int i = 0; i < input.length; i++){
            input[i] = t[i];//解封裝
        }
    }
    //double[]數組的重載函數
    public static void sort(double[] input){
        Double[] t = new Double[input.length];
        for(int i = 0; i < input.length; i++){
            t[i] = input[i];
        }
        quickSort(t,0,t.length);
        for(int i = 0; i < input.length; i++){
            input[i] = t[i];
        }
    }
    //byte[]數組的重載函數
    public static void sort(byte[] input){
        Byte[] t = new Byte[input.length];
        for(int i = 0; i < input.length; i++){
            t[i] = input[i];
        }
        quickSort(t,0,t.length);
        for(int i = 0; i < input.length; i++){
            input[i] = t[i];
        }
    }
    //short[]數組的重載函數
    public static void sort(short[] input){
        Short[] t = new Short[input.length];
        for(int i = 0; i < input.length; i++){
            t[i] = input[i];
        }
        quickSort(t,0,t.length);
        for(int i = 0; i < input.length; i++){
            input[i] = t[i];
        }
    }
    //char[]數組的重載函數
    public static void sort(char[] input){
        Character[] t = new Character[input.length];
        for(int i = 0; i < input.length; i++){
            t[i] = input[i];
        }
        quickSort(t,0,t.length);
        for(int i = 0; i < input.length; i++){
            input[i] = t[i];
        }
    }
    //float[]數組的重載函數
    public static void sort(float[] input){
        Float[] t = new Float[input.length];
        for(int i = 0; i < input.length; i++){
            t[i] = input[i];
        }
        quickSort(t,0,t.length);
        for(int i = 0; i < input.length; i++){
            input[i] = t[i];
        }
    }
    
    //測試用的main函數
     public static void main(String[] args) {
        //生產一個隨機數組成的int[]數組,用來測試
        int LEN = 10;
        int[] input = new int[LEN];
        Random r = new Random();
        System.out.print("int[] before sorting: ");
        for(int i = 0; i < input.length; i++) {
            input[i] = r.nextInt(10*LEN);
            System.out.print(input[i] + " ");
        }
        System.out.println();
        System.out.print("int[] after sorting: ");
        sort(input);
        for(int i : input) {
          System.out.print(i + " ");
        } 
        System.out.println();

        //生成一個字符串數組,用來測試
        String[] s = new String[]{"b","a","e","d","f","c"};
        System.out.print("String[] before sorting: ");
        for(int i = 0; i < s.length; i++) {
            System.out.print(s[i] + " ");
        }
        System.out.println();
        System.out.print("String[] after sorting: ");
        sort(s);
        for(int i = 0; i < s.length; i++) {
            System.out.print(s[i] + " ");
        }
        System.out.println();
        
        //生成一個字符串列表,用來測試
        List<String> l = new LinkedList<String>();
        s = new String[]{"b","a","e","d","f","c"};
        System.out.print("LinkedList<String> before sorting: ");
        for (int j=0; j<s.length; j++) {
            l.add(s[j]);
            System.out.print(s[j] + " ");
        }
        System.out.println();
        sort(l);
        System.out.print("LinkedList<String> after sorting: ");
        for (String ts : l) {
            System.out.print(ts + " ");
        }
        System.out.println();
    }
}


運行main函數測試,從輸出能夠看出QuickSort類工做正常: 函數

int[] before sorting: 65 48 92 26 3 8 59 21 16 45 
int[] after sorting: 3 8 16 21 26 45 48 59 65 92 
String[] before sorting: b a e d f c 
String[] after sorting: a b c d e f 
LinkedList<String> before sorting: b a e d f c 
LinkedList<String> after sorting: a b c d e f


參考資料: 性能

[1] 麻省理工學院公開課:算法導論> 快排及隨機化算法
http://v.163.com/movie/2010/12/S/4/M6UTT5U0I_M6V2T7IS4.html 測試

[2] Java官方API(Oracle Java SE7)源代碼,下載安裝JDK後,源代碼位於安裝根目錄的src.zip文件中
http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html 優化

[3] OpenJDK源代碼下載(包括了HotSpot虛擬機、各個系統下API的源代碼,其中API源代碼位於openjdk\jdk\src\share\classes文件夾下):
https://jdk7.java.net/source.html

相關文章
相關標籤/搜索