八大排序算法最全總結

排序算法概念

排序也稱排序算法?(Sort Algorithm),排序是將一?組數據,依指定的順序進行排列?的過程。

排序的分類:

  1. 內部排序:
    指將需要處理的所有數據都加載?到內部存儲器中進行排序。
  2. 外部排序法:
    數據量過大,無法全部加載到內?存中,需要借助外部存儲進行
    排序。
  3. 常見的排序算法分類(見右圖):
image

時間復雜度

要知道各個算法的執行性能和優劣,需要理解時間復雜度是如何計算和概念以及根據時間復雜度選取適合的算法進行排序。可以看下面這篇文章:

算法優劣指標之時間復雜度如何計算總結:

https://juejin.im/post/5d81fcf6e51d4561c94b107a

八大算法的時間復雜度總結

image

冒泡排序

基本介紹

冒泡排序(Bubble Sorting)的基本思想是:通過對待排序序列從前向后(從下標較小的元素開始),依次比較相鄰元素的值,若發現逆序則交換,使值較大的元素逐漸從前移向后部,就象水底下的氣泡一樣逐漸向上冒。

優化

因為排序的過程中,各元素不斷接近自己的位置,如果一趟比較下來沒有進行過交換,就說明序列有序,因此要在排序過程中設置一個標志flag判斷元素是否進行過交換。從而減少不必要的比較。(這里說的優化,可以在冒泡排序寫好后,在進行)。

小結冒泡排序規則

(1) 一共進行 數組的大小-1 次 大的循環
(2)每一趟排序的次數在逐漸的減少
(3) 如果我們發現在某趟排序中,沒有發生一次交換, 可以提前結束冒泡排序。這個就是優化

image

代碼演示(優化后)

/**
 * @author zeng
 * @version 2019/4/29
 * @description: 冒泡排序
 */
public class BubbleSort {

    /**
     *  比較相鄰的元素。如果第一個比第二個大,就交換它們兩個;
     * 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對,這樣在最后的元素應該會是最大的數;
     * 針對所有的元素重復以上的步驟,除了最后一個;
     *
     * 最佳情況:T(n) = O(n)   最差情況:T(n) = O(n2)
     *        最壞情況:       O(n2)
     *        空間復雜度:     O(1)
     *        穩定性:         穩定
     *
     */
    public static void sort(int []a){

        int len=a.length;
        //臨時變量
        int temp = 0;
        //標識變量
        boolean flag = false;
        for(int i=0;i<len;i++){

            for(int j=0;j<len-i-1;j++){
                  if(a[j]>a[j+1]){
                      flag = true;
                      temp=a[j];
                      a[j]=a[j+1];
                      a[j+1]=temp;
                  }
            }
            System.out.println(Arrays.toString(a));
            //如果沒有發生變化,表示在改成查詢中一次都沒有交換過,說明已經是有序的,則不需要再遍歷了
            if(!flag){
                break;
            } else {
              flag = false;
            }
        }
    }

    public static void main(String[] args) {

//      int[] a={54,45,32,20,26,43,4,77,3,1,34,21,17,18,8,4,5,8,15,38};
        int[] a = {1,2,3,7,6,5};
        long currentTimeMillis = System.nanoTime();
        System.out.println(currentTimeMillis);
        sort(a);
        long finishTime = System.nanoTime();
        System.out.println(finishTime);
        System.out.println("冒泡排序算法時間:"+(finishTime-currentTimeMillis));
        System.out.println(Arrays.toString(a));
        Arrays.sort(a);
    }
}

結果

image

可以看到在第三次遍歷時,已經已經是有序的,所以不需要在繼續遍歷了。這是優化后的結果。

選擇8000個隨機數,進行排序,獲取結果如下:

image

時間空間復雜度

由代碼核心循環執行的代碼可知:

image

時間復雜度為 O(n^2),平均時間 O(n^2).

選擇排序

選擇式排序也屬于內部排序法,是從欲排序的數據中,按指定的規則選出某一元素,再依規定交換位置后達到排序的目的。

基本思想

第一次從arr[0]arr[n-1]中選取最小值,與arr[0]交換,第二次從arr[1]arr[n-1]中選取最小值,與arr[1]交換,第三次從arr[2]arr[n-1]中選取最小值,與arr[2]交換,…,第i次從arr[i-1]arr[n-1]中選取最小值,與arr[i-1]交換,…, 第n-1次從arr[n-2]~arr[n-1]中選取最小值,與arr[n-2]交換,總共通過n-1次,得到一個按排序碼從小到大排列的有序序列。

image

代碼示例:

public class StraightSelectSort {

    /**
     * 常用于取序列中最大最小的幾個數時。
     *
     * (如果每次比較都交換,那么就是交換排序;如果每次比較完一個循環再交換,就是簡單選擇排序。)
     *
     * 遍歷整個序列,將最小的數放在最前面。
     *
     * 遍歷剩下的序列,將最小的數放在最前面。
     *
     * 重復第二步,直到只剩下一個數。
     *
     *    平均時間復雜度: O(n2)
     *      *  最好情況:       O(n2)
     *      *  最壞情況:       O(n2)
     *      *  空間復雜度:     O(1)
     *      *  穩定性:         穩定
     *      *
     */
    public static void sort(int []a){
        int len=a.length;
        int value=0;
        int position=0;
        for(int i=0;i<len;i++){
            value=a[i];
            position=i;
            int k=i+1;
            while(k<len){
                // 說明假定的最小值,并不是最小
                if(a[k]<value){
                    //交換
                    value=a[k];
                    position=k;
                }
                k++;
            }
            // 將最小值,放在arr[0], 即交換,這里也可以判斷一下,是否最小值相等,也可以不做判斷,直接。
            if(a[position] != a[i])
            a[position]=a[i];
            a[i]=value;
        }
        System.out.println(Arrays.toString(a));
    }

    public static void main(String[] args) {

    //    int  []a={54,45,32,20,26,43,4,77,3,1,34,21,17,18,8,4,5,8,15,38};
        //創建要給80000個的隨機的數組
        int[] arr = new int[80000];
        for (int i = 0; i < 80000; i++) {
            // 生成一個[0, 8000000) 數
            arr[i] = (int) (Math.random() * 8000000);
        }
        long currentTimeMillis = System.nanoTime();
        System.out.println(currentTimeMillis);

        Date data1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(data1);
        System.out.println("排序前的時間是=" + date1Str);
        sort(arr);
        long finishTime = System.nanoTime();
        System.out.println(finishTime);
        Date data2 = new Date();
        String date2Str = simpleDateFormat.format(data2);
        System.out.println("排序前的時間是=" + date2Str);

        System.out.println("簡單選擇排序算法時間:"+(finishTime-currentTimeMillis));
        System.out.println(Arrays.toString(arr));
    }
}

結果:

8000個隨機數排序結果如下:


image

時間復雜度

image

這里使用while,但跟for是差不多的,都是N次。
平均時間復雜度: O(n2)
* * 最好情況: O(n2)
* * 最壞情況: O(n2)
* * 空間復雜度: O(1)
* * 穩定性: 穩定

插入排序

插入式排序屬于內部排序法,是對于欲排序的元素以插入的方式找尋該元素的適當位置,以達到排序的目的。

基本思想

插入排序(Insertion Sorting)的基本思想是:把n個待排序的元素看成為一個有序表和一個無序表,開始時有序表中只包含一個元素,無序表中包含有n-1個元素,排序過程中每次從無序表中取出第一個元素,把它的排序碼依次與有序表元素的排序碼進行比較,將它插入到有序表中的適當位置,使之成為新的有序表。

image

代碼演示

  /**
     *  我們經常會到這樣一類排序問題:把新的數據插入到已經排好的數據列中。將第一個數和第二個數排序,
     *  然后構成一個有序序列將第三個數插入進去,構成一個新的有序序列。對第四個數、第五個數……
     *  直到最后一個數,重復第二步。如題所示:
     *
     *  直接插入排序(Straight Insertion Sorting)的基本思想:在要排序的一組數中,假設前面(n-1) [n>=2] 個數已經是排好順序的,
     *  現在要把第n個數插到前面的有序數中,使得這n個數也是排好順序的。如此反復循環,直到全部排好順序。
     *
     * 假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,
     * 即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,則稱這種排序算法是穩定的;否則稱為不穩定的。
     *  n2 代表n的二次方
     *
     *  平均時間復雜度: O(n2)
     *  最好情況:       O(n)
     *  最壞情況:       O(n2)
     *  空間復雜度:     O(1)
     *  穩定性:         穩定
     *
     */
    public static void sort(int []a){

        int length = a.length;
        //可知分為兩部分,一部分有序,一部分無序,所以遍歷要從第二個開始
        for(int i=1;i<length;i++){
            //把要插入的值保存起來
            int insertVal = a[i];
            int insertIndex = i-1;
            //比較大小并替換值
            while(insertIndex >= 0 && insertVal < a[insertIndex]){
                a[insertIndex+1]=a[insertIndex];
                insertIndex--;
            }
            //把值插入排序好的位置
            a[insertIndex+1]=insertVal;
        }
    }

    public static void main(String[] args) {
        /*int  []a={54,45,32,20,26,43,4,77,3,1,34,21,17,18,8,4,5,8,15,38};
        long currentTimeMillis = System.nanoTime();
        System.out.println(currentTimeMillis);
        sort(a);
        long finishTime = System.nanoTime();
        System.out.println(finishTime);
        System.out.println("直接插入排序算法時間:"+(finishTime-currentTimeMillis));
        System.out.println(Arrays.toString(a));*/

        //創建要給80000個的隨機的數組
        int[] arr = new int[80000];
        for (int i = 0; i < 80000; i++) {
            arr[i] = (int) (Math.random() * 8000000); // 生成一個[0, 8000000) 數
        }

        System.out.println("排序前");
        //System.out.println(Arrays.toString(arr));

        Date data1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(data1);
        System.out.println("排序前的時間是=" + date1Str);

        sort(arr);

        Date data2 = new Date();
        String date2Str = simpleDateFormat.format(data2);
        System.out.println("排序后的時間是=" + date2Str);
        System.out.println(Arrays.toString(arr));
    }
}

結果

取8000隨機數

image

可以看到對比之前的效率高了很多。

時間復雜度

平均時間復雜度: O(n^2)
* 最好情況: O(n)
* 最壞情況: O(n^2)
* 空間復雜度: O(1)
* 穩定性: 穩定

存在的問題

當進行升序排序時,如果最后一個數的值最小,那需要往前移動的次數會很多。效率不高。由此,引出下面的希爾排序。

希爾排序

希爾排序是希爾(Donald Shell)于1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡單插入排序經過改進之后的一個更高效的版本,也稱為縮小增量排序。

基本思想

希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序算法排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。

代碼演示

交換式的shell排序
希爾排序嚴格來說是基于插入排序的思想,又被稱為縮小增量排序。
     *   具體流程如下:
     *   1、將包含n個元素的數組,分成k=n/2個數組序列,第一個數據和第n/2+1個數據為一對,第二個和第n/2+2為一對,比較...
     *   2、對每對數據進行比較和交換,排好順序;
     *   3、然后分成n/4個數組序列,再次排序;
     *   4、不斷重復以上過程,隨著序列減少并直至為1,排序完成。
     *
     * 假如有初始數據:25  11  45  26  12  78。
     *   1、第一輪排序,將該數組分成 6/2=3 個數組序列,第1個數據和第4個數據為一對,第2個數據和第5個數據為一對,
     *      第3個數據和第6個數據為一對,每對數據進行比較排序,排序后順序為:[25, 11, 45, 26, 12, 78]。
     *   2、第二輪排序 ,將上輪排序后的數組分成6/4=1個數組序列,此時逐個對數據比較,按照插入排序對該數組進行排序,排序后的順序為:[11, 12, 25, 26, 45, 78]。
     *
     *   對于插入排序而言,如果原數組是基本有序的,那排序效率就可大大提高。另外,對于數量較小的序列使用直接插入排序,會因需要移動的數據量少,
     *     其效率也會提高。因此,希爾排序具有較高的執行效率。
     *
     *     希爾排序并不穩定,O(1)的額外空間,時間復雜度為O(N*(logN)^2)。
public class ShellSortTest01 {

    // 使用逐步推導的方式來編寫希爾排序
    // 希爾排序時, 對有序序列在插入時采用交換法,
    // 思路(算法) ===> 代碼
    public static void sort(int[] arr){

        int length = arr.length;
        int temp;
        // 根據前面的逐步分析,使用循環處理
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                // 遍歷各組中所有的元素(共gap組,每組有個元素), 步長gap
                for (int j = i - gap; j >= 0; j -= gap) {
                    // 如果當前元素大于加上步長后的那個元素,說明交換
                    if(arr[j]>arr[j+gap]){
                        temp=arr[j];
                        arr[j]=arr[j+gap];
                        arr[j+gap]=temp;
                    }
                }
            }
        //    System.out.println(Arrays.toString(arr));
        }
    }



    public static void main(String[] args) {
    //    int[] arr = { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };

        // 創建要給80000個的隨機的數組
       int[] arr = new int[80000];
       for (int i = 0; i < 80000; i++) {
           arr[i] = (int) (Math.random() * 8000000); // 生成一個[0, 8000000) 數
       }

        System.out.println("排序前");
        Date data1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(data1);
        System.out.println("排序前的時間是=" + date1Str);

        //shellSort(arr); //交換式
        sort(arr);//移位方式
        System.out.println(Arrays.toString(arr));
        Date data2 = new Date();
        String date2Str = simpleDateFormat.format(data2);
        System.out.println("排序后的時間是=" + date2Str);

        //System.out.println(Arrays.toString(arr));
    }
}

演示結果:

image
優化后

移位法優化

    //對交換式的希爾排序進行優化->移位法
    public static void shellSort2(int[] arr) {

        // 增量gap, 并逐步的縮小增量
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            // 從第gap個元素,逐個對其所在的組進行直接插入排序
            for (int i = gap; i < arr.length; i++) {
                int j = i;
                int temp = arr[j];
                if (arr[j] < arr[j - gap]) {
                    while (j - gap >= 0 && temp < arr[j - gap]) {
                        //移動
                        arr[j] = arr[j-gap];
                        j -= gap;
                    }
                    //當退出while后,就給temp找到插入的位置
                    arr[j] = temp;
                }

            }
        }
    }

演示結果:
同等條件下運行:


image

可以看到效果由10秒變成了1秒。這是很可觀的。

時間復雜度

希爾排序并不穩定,O(1)的額外空間,時間復雜度為O(N*(logN)^2)

image

快速排序

介紹

快速排序(Quicksort)是對冒泡排序的一種改進。基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列

image

步驟:

  1. 隨機選一個值作為參考值,第一輪,選0,作為參考值
  2. 分別從左邊和右邊開始輪詢,找到比0比較大的值和比較小的值,進行交換,小的放左邊,大的放右邊,左邊的索引遞增,右邊的遞減,左邊必須大于右邊,如果相等或小于,則退出循環。
  3. 進行新的一輪循環,第一輪會分為左右兩塊,以0的值作為參考值。采用遞歸,繼續分成兩邊去重復第二部的步驟,直到左邊的索引已經超過要排序的數組的長度了(左邊是遞增的),以及右邊的索引小于要排序的數組的初始索引。

代碼分析

public class QuickSortTest01 {

    /**
     * 快速排序的基本思想:1、先從數列中取出一個數作為基準數
     *
     * 2、分區過程,將比這個數大的數全放到它的右邊,小于或等于它的數全放到它的左邊
     *
     * 3、再對左右區間重復第二步,直到各區間只有一個數.
     *平均時間復雜度:
     *        最好情況:   o(nlogn)
     *        最壞情況:    o(nlog2n)
     *       空間復雜度:    o(1)
     *        穩定性:        不穩定

     *
     */
    public static void sort(int []arr,int start,int end){

        if(start<end) {
            //中軸值
            int baseNum = arr[start];
            int left = start;
            int right = end;

            int temp;
            do {
                //從左邊開始,定位到比中軸值大的數
                while (arr[left] < baseNum && left < end) {
                    left++;
                }
                //從右邊開始定位到比中軸數,小的數
                while (arr[right] > baseNum && right > start) {
                    right--;
                }
                //如果左邊的索引小于右邊數的索引,則交換
                if (left <= right) {
                    temp = arr[left];
                    arr[left] = arr[right];
                    arr[right] = temp;

                    left++;
                    right--;
                }
            } while (left <= right);
            if(left<end){
                sort(arr,left,end);
            }
            if(right> start){
                sort(arr,start,right);
            }
        }
    }

    public static void main(String[] args) {

        int[] arr = {-9,78,0,23,-567,70, -1,900, 4561};
//        int[] arr = new int[8000000];
//        for (int i = 0; i < 8000000; i++) {
//            arr[i] = (int) (Math.random() * 8000000); // 生成一個[0, 8000000) 數
//        }

        System.out.println("排序前");
        Date data1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(data1);

        System.out.println("排序前的時間是=" + date1Str);
        long currentTimeMillis = System.nanoTime();
        sort(arr, 0, arr.length-1);
        System.out.println(Arrays.toString(arr));
        long finishTime = System.nanoTime();
        System.out.println("快速排序算法時間:"+(finishTime-currentTimeMillis));
        Date data2 = new Date();
        String date2Str = simpleDateFormat.format(data2);
        System.out.println("排序前的時間是=" + date2Str);
    }
}

還有另一種寫法,我做過對比

public static void quickSort(int[] arr,int left, int right) {
        int l = left; //左下標
        int r = right; //右下標
        //pivot 中軸值
        int pivot = arr[(left + right) / 2];
        int temp = 0; //臨時變量,作為交換時使用
        //while循環的目的是讓比pivot 值小放到左邊
        //比pivot 值大放到右邊
        while( l < r) { 
            //在pivot的左邊一直找,找到大于等于pivot值,才退出
            while( arr[l] < pivot) {
                l += 1;
            }
            //在pivot的右邊一直找,找到小于等于pivot值,才退出
            while(arr[r] > pivot) {
                r -= 1;
            }
            //如果l >= r說明pivot 的左右兩的值,已經按照左邊全部是
            //小于等于pivot值,右邊全部是大于等于pivot值
            if( l >= r) {
                break;
            }
            
            //交換
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            
            //如果交換完后,發現這個arr[l] == pivot值 相等 r--, 前移
            if(arr[l] == pivot) {
                r -= 1;
            }
            //如果交換完后,發現這個arr[r] == pivot值 相等 l++, 后移
            if(arr[r] == pivot) {
                l += 1;
            }
        }
        
        // 如果 l == r, 必須l++, r--, 否則為出現棧溢出
        if (l == r) {
            l += 1;
            r -= 1;
        }
        //向左遞歸
        if(left < r) {
            quickSort(arr, left, r);
        }
        //向右遞歸
        if(right > l) {
            quickSort(arr, l, right);
        }
    }
結果對比

第一種代碼的結果

image

第二種代碼的結果


image

快了百分之50以上,建議使用第一種,還有優化的可以私聊告訴我。總體的思路是一樣的。

時間復雜度

image

image

歸并算法

介紹

歸并排序(MERGE-SORT)是利用歸并的思想實現的排序方法,該算法采用經典的分治(divide-and-conquer)策略(分治法將問題分(divide)成一些小的問題然后遞歸求解,而治(conquer)的階段則將分的階段得到的各答案"修補"在一起,即分而治之)。

可以看到這種結構很像一棵完全二叉樹,本文的歸并排序我們采用遞歸去實現(也可采用迭代的方式去實現)。分階段可以理解為就是遞歸拆分子序列的過程。
歸并排序思想示意圖1-基本思想:


image

歸并排序思想示意圖2-合并相鄰有序子序列:
再來看看治階段,我們需要將兩個已經有序的子序列合并成一個有序序列,比如上圖中的最后一次合并,要將[4,5,7,8]和[1,2,3,6]兩個已經有序的子序列,合并為最終序列[1,2,3,4,5,6,7,8],來看下實現步驟

image

代碼演示:

public class MergeSort {

    /**
     * Merge sort
     * 速度僅次于快速排序,內存少的時候使用,可以進行并行計算的時候使用。
     * 選擇相鄰兩個數組成一個有序序列。
     * 選擇相鄰的兩個有序序列組成一個有序序列。
     * 重復第二步,直到全部組成一個有序序列。
     *
     * 最佳情況:T(n) = O(n)   最差情況:T(n) = O(n2)
     *        最壞情況:       O(n2)
     *        空間復雜度:     O(1)
     *        穩定性:         穩定
     *
     */
    public static void mergeSort(int []arr,int left,int right,int[] temp){

        if(left < right) {
            int mid = (left+right) / 2;

            mergeSort(arr,left,mid,temp);
            mergeSort(arr,mid+1,right,temp);

            merge(arr,left,mid,right,temp);

        }

    }

    public static void merge(int[] arr,int left,int mid,int right,int[] temp){

            // 初始化i左邊有序序列得初始索引
            int i=left;
            // 初始化j右邊有序序列初始索引
            int j=mid+1;
            //temp的初識索引
            int t=0;

            // 將兩邊的索引進行排序
            while(i<=mid && j<= right){
                if(arr[i] <= arr[j]){
                    temp[t++]=arr[i];
                    i++;
                } else{
                    temp[t++]=arr[j];
                    j++;
                }
            }


            //將兩邊剩余的值加入temp的值
            while(i<=mid){
                temp[t++]=arr[i];
                i++;
            }

            while(j <= right){
                temp[t++]=arr[j];
                j++;
            }

            //把temp中的值加入arr中。
            int templeft=left;
            int t1=0;

            while(templeft <= right){
                arr[templeft]=temp[t1++];
                templeft++;
            }
    }

    public static void main(String[] args) {
      //  int a[] = { 8, 4, 5, 7, 1, 3, 6, 2 };
      //  int  []a={54,45,32,20,26,43,4,77,3,1,34,21,17,18,8,4,5,8,15,38};
//        long currentTimeMillis = System.nanoTime();
//        System.out.println(currentTimeMillis);
//        mergeSort(a);
//        long finishTime = System.nanoTime();
//        System.out.println(finishTime);
//        System.out.println("歸并排序算法時間:"+(finishTime-currentTimeMillis));
//        System.out.println(Arrays.toString(a));

        //int arr[] = { 8, 4, 5, 7, 1, 3, 6, 2 }; //

        //測試快排的執行速度
        // 創建要給80000個的隨機的數組
        int[] a = new int[8000000];
        for (int i = 0; i < 8000000; i++) {
            a[i] = (int) (Math.random() * 8000000); // 生成一個[0, 8000000) 數
        }
        System.out.println("排序前");
        Date data1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(data1);
        System.out.println("排序前的時間是=" + date1Str);

        int temp[] = new int[a.length]; //歸并排序需要一個額外空間
        long currentTimeMillis = System.nanoTime();
        System.out.println(currentTimeMillis);
        mergeSort(a, 0, a.length - 1, temp);
        long finishTime = System.nanoTime();
        System.out.println(finishTime);
        System.out.println("歸并排序算法時間:"+(finishTime-currentTimeMillis));
        System.out.println(Arrays.toString(a));

        Date data2 = new Date();
        String date2Str = simpleDateFormat.format(data2);
        System.out.println("排序前的時間是=" + date2Str);

        //System.out.println("歸并排序后=" + Arrays.toString(arr));
    }
}

結果演示:

image

時間復雜度

  • 最佳情況:T(n) = O(n) 最差情況:T(n) = O(n2)
    •    最壞情況:       O(n2)
      
    •    空間復雜度:     O(1)
      
    •    穩定性:         穩定
      
image

image

基數排序

基數排序(桶排序)介紹

基數排序(radix sort)屬于“分配式排序”(distribution sort),又稱“桶子法”(bucket sort)或bin sort,顧名思義,它是通過鍵值的各個位的值,將要排序的元素分配至某些“桶”中,達到排序的作用

基數排序法是屬于穩定性的排序,基數排序法的是效率高的穩定性排序法

基數排序(Radix Sort)是桶排序的擴展

基數排序是1887年赫爾曼·何樂禮發明的。它是這樣實現的:將整數按位數切割成不同的數字,然后按每個位數分別比較。

基本思想

將所有待比較數值統一為同樣的數位長度,數位較短的數前面補零。然后,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以后, 數列就變成一個有序序列。

看一個圖文解釋,理解基數排序的步驟

image

(數的位數達不到個位以上的,在每次桶排序輪空后,依次加入第一個桶)。

代碼演示:

public class RadixSort {

    public static void main(String[] args) {
        int arr[] = { 53, 3, 542, 748, 14, 214};
        
        // 80000000 * 11 * 4 / 1024 / 1024 / 1024 =3.3G 
//      int[] arr = new int[8000000];
//      for (int i = 0; i < 8000000; i++) {
//          arr[i] = (int) (Math.random() * 8000000); // 生成一個[0, 8000000) 數
//      }
        System.out.println("排序前");
        Date data1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(data1);
        System.out.println("排序前的時間是=" + date1Str);
        
        radixSort(arr);
        
        Date data2 = new Date();
        String date2Str = simpleDateFormat.format(data2);
        System.out.println("排序前的時間是=" + date2Str);
        
        System.out.println("基數排序后 " + Arrays.toString(arr));
        
    }

    //基數排序方法
    public static void radixSort(int[] arr) {
        
        //根據前面的推導過程,我們可以得到最終的基數排序代碼
        
        //1. 得到數組中最大的數的位數
        int max = arr[0]; //假設第一數就是最大數
        for(int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        //得到最大數是幾位數
        int maxLength = (max + "").length();
        
        
        //定義一個二維數組,表示10個桶, 每個桶就是一個一維數組
        //說明
        //1. 二維數組包含10個一維數組
        //2. 為了防止在放入數的時候,數據溢出,則每個一維數組(桶),大小定為arr.length
        //3. 名明確,基數排序是使用空間換時間的經典算法
        int[][] bucket = new int[10][arr.length];
        
        //為了記錄每個桶中,實際存放了多少個數據,我們定義一個一維數組來記錄各個桶的每次放入的數據個數
        //可以這里理解
        //比如:bucketElementCounts[0] , 記錄的就是  bucket[0] 桶的放入數據個數
        int[] bucketElementCounts = new int[10];
        
        
        //這里我們使用循環將代碼處理
        
        for(int i = 0 , n = 1; i < maxLength; i++, n *= 10) {
            //(針對每個元素的對應位進行排序處理), 第一次是個位,第二次是十位,第三次是百位..
            for(int j = 0; j < arr.length; j++) {
                //取出每個元素的對應位的值
                int digitOfElement = arr[j] / n % 10;
                //放入到對應的桶中
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }
            //按照這個桶的順序(一維數組的下標依次取出數據,放入原來數組)
            int index = 0;
            //遍歷每一桶,并將桶中是數據,放入到原數組
            for(int k = 0; k < bucketElementCounts.length; k++) {
                //如果桶中,有數據,我們才放入到原數組
                if(bucketElementCounts[k] != 0) {
                    //循環該桶即第k個桶(即第k個一維數組), 放入
                    for(int l = 0; l < bucketElementCounts[k]; l++) {
                        //取出元素放入到arr
                        arr[index++] = bucket[k][l];
                    }
                }
                //第i+1輪處理后,需要將每個 bucketElementCounts[k] = 0 !!!!
                bucketElementCounts[k] = 0;
                
            }
            //System.out.println("第"+(i+1)+"輪,對個位的排序處理 arr =" + Arrays.toString(arr));
            
        }
    }
}

運行結果

image

補充說明

基數排序是對傳統桶排序的擴展,速度很快.
基數排序是經典的空間換時間的方式,占用內存很大, 當對海量數據排序時,容易造成 OutOfMemoryError 。
基數排序時穩定的。[注:假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,則稱這種排序算法是穩定的;否則稱為不穩定的]
有負數的數組,我們不用基數排序來進行排序, 如果要支持負數,參考:
https://code.i-harness.com/zh-CN/q/e98fa9

時間復雜度

image

堆排序算法

http://www.lxweimin.com/p/51bbf3717493

待續

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容

  • 查找和排序算法是算法的入門知識,其經典思想可以用于很多算法當中。因為其實現代碼較短,應用較常見。所以在面試中經常會...
    JourWon閱讀 2,250評論 0 34
  • 參考:十大經典排序算法 0、排序算法說明 0.1排序的定義 對一序列對象根據某個關鍵字進行排序。 0.2 術語說明...
    誰在烽煙彼岸閱讀 1,021評論 0 12
  • 排序算法說明 (1)排序的定義:對一序列對象根據某個關鍵字進行排序; 輸入:n個數:a1,a2,a3,…,an 輸...
    code武閱讀 669評論 0 0
  • Ba la la la ~ 讀者朋友們,你們好啊,又到了冷鋒時間,話不多說,發車! 1.冒泡排序(Bub...
    王飽飽閱讀 1,808評論 0 7
  • 張大千,可以說是中國最具傳奇色彩的藝術家,他對于中國的藝術推廣到世界有著不可磨滅的貢獻。而且張大千是一個全能型的畫...
    有趣的歷史段子閱讀 1,552評論 11 18