1. 程式人生 > >Java中8種常見的排序方法

Java中8種常見的排序方法

思想 vat 左右 排好序 完全二叉樹 load linu 歸並 pack

排序方法的演示
1)插入排序(直接插入排序、希爾排序)
2)交換排序(冒泡排序、快速排序)
3)選擇排序(直接選擇排序、堆排序)
4)歸並排序
5)分配排序(基數排序)
所需輔助空間最多:歸並排序
所需輔助空間最少:堆排序
平均速度最快:快速排序
不穩定:快速排序,希爾排序,堆排序。

1.插入排序

1.1.基本思想

  直接插入排序的基本操作是將一個記錄插入到已經排好的有序表中,從而得到一個新的、記錄數增1的有序表。對於給定的一組記錄,初始時假定第一個記錄自成一個有序序列,其余記錄為無序序列。接著從第二個記錄開始,按照記錄的大小依次將當前處理的記錄插入到其之前的有序序列中,直到最後一個記錄插到有序序列中為止。

1.2.復雜度分析

  當最好的情況,也就是要排序的表本身就是有序的,此時只有數據比較,沒有數據移動,時間復雜度為O(n)
當最壞的情況,即待排序的表是逆序的情況,此時需要比較次數為:2+3+…+n=(n+2)(n-1)/2 次,而記錄移動的最大值也達到了 (n+4)(n-1)/2 次.
如果排序記錄是隨機的,那麽根據概率相同的原則,平均比較和移動次數約為次n2/4,因此,得出直接插入排序發的時間復雜度為技術分享圖片。從這裏可以看出,同樣的是時間復雜度技術分享圖片,直接插入排序法比冒泡和簡單選擇排序的性能要好一些。

1.3.java實現

package Sort;

import java.util.Arrays;

public class InsertSort { public static void main(String[] args) { int[] arr = { 5, 6, 1, 2, 4, 0, 13, 4, 20, 1, 3, 7, 8, 6 }; insertSort(arr); System.out.println(Arrays.toString(arr)); System.out.println(true&&true); } public static void insertSort(int
[] arr) { // 遍歷所有數字 for (int i = 1; i < arr.length; i++) { int temp = arr[i]; int j; if(arr[i-1]>temp) { for(j=i;j>=1&&arr[j-1]>temp;j--) {//不要寫成arr[j-1]>temp&&j>0.因為當j=0的時候,表達式會先判斷arr[0-1]>temp,然後再去判斷後面的j>0,這樣會報指針越界異常.但是如果先判斷j>0,此時j=0,j>0判斷的時候結果為false,便不會去判斷arr[0-1]>temp這個表達式,也就不會出現arr[-1]的情況 arr[j]=arr[j-1]; } arr[j]=temp; } } } }

2.希爾排序

2.1.基本思想

  希爾排序也成為“縮小增量排序”,其基本原理是,現將待排序的數組元素分成多個子序列,使得每個子序列的元素個數相對較少,然後對各個子序列分別進行直接插入排序,待整個待排序列“基本有序”後,最後在對所有元素進行一次直接插入排序。因此,我們要采用跳躍分割的策略:將相距某個“增量”的記錄組成一個子序列,這樣才能保證在子序列內分別進行直接插入排序後得到的結果是基本有序而不是局部有序。希爾排序是對直接插入排序算法的優化和升級。
  所謂的基本有序,就是小的關鍵字基本在前面,大的基本在後面,不大不小的基本在中間,例如{2,1,3,6,4,7,5,8,9,}就可以稱為基本有序了。但像{1,5,9,3,7,8,2,4,6}這樣,9在第三位,2在倒數第三位就談不上基本有序。

2.2.復雜度分析

  希爾排序的關鍵並不是隨便分組後各自排序,而是將相隔某個“增量”的記錄組成一個子序列,實現跳躍式移動,使得排序的效率提高。需要註意的是,增量序列的最後一個增量值必須等於1才行。另外,由於記錄是跳躍式的移動,希爾排序並不是一種穩定的排序算法。
希爾排序最好時間復雜度和平均時間復雜度都是技術分享圖片,最壞時間復雜度為技術分享圖片

2.3.java實現

package Sort;

import java.util.Arrays;

public class ShellSort {

    public static void main(String[] args) {
        int[] arr = { 5, 6, 1, 2, 4, 0, 13, 4, 20, 1, 3, 7, 8, 6 };
        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void shellSort(int[] arr) {
        // 遍歷所有的步長(希爾增量序列)
        for (int d = arr.length / 2; d > 0; d /= 2) {
            // 遍歷所有元素(以下兩個for循環就是插入排序,把1換成了d)
            for (int i = d; i < arr.length; i++) {
                int temp = arr[i];
                int j;
                if (arr[i - d] > temp) {
                    for (j = i; j >= d && arr[j - d] > temp; j -= d) {
                        arr[j] = arr[j - d];
                    }
                    arr[j] = temp;
                }
            }
        }
    }
}

3.冒泡排序

3.1.基本思想

  依次比較相鄰的兩個數,將小數放在前面,大數放在後面。即在第一趟:首先比較第1個和第2個數,將小數放前,大數放後。然後比較第2個數和第3個數,將小數放前,大數放後,如此繼續,直至比較最後兩個數,將小數放前,大數放後。至此第一趟結束,將最大的數放到了最後。在第二趟:仍從第一對數開始比較(因為可能由於第2個數和第3個數的交換,使得第1個數不再小於第2個數),將小數放前,大數放後,一直比較到倒數第二個數(倒數第一的位置上已經是最大的),第二趟結束,在倒數第二的位置上得到一個新的最大數(其實在整個數列中是第二大的數)。如此下去,重復以上過程,直至最終完成排序。

  用二重循環實現,外循環變量設為i,內循環變量設為j。假如有n個數需要進行排序,則外循環重復n-1次,內循環依次重復n-1,n-2,...,1次。每次進行比較的兩個元素都是與內循環j有關的,它們可以分別用a[j]和a[j+1]標識,i的值依次為1,2,...,n-1,對於每一個i,j的值依次為0,1,2,...n-i 。

  設數組長度為N:
  1.比較相鄰的前後二個數據,如果前面數據大於後面的數據,就將二個數據交換。
  2.這樣對數組的第0個數據到N-1個數據進行一次遍歷後,最大的一個數據就“沈”到數組第N-1個位置。
  3.N=N-1,如果N不為0就重復前面二步,否則排序完成。

3.2.復雜度分析

  時間復雜度分析。其外層循環執行 N - 1次。內層循環最多的時候執行N次,最少的時候執行1次,平均執行 (N+1)/2次。
  所以循環體內的比較交換約執行 (N - 1)(N + 1) / 2 = (N^2 - 1)/2(其中N^2是仿照Latex中的記法,表示N的平方)。按照計算復雜度的原則,去掉常數,去掉最高項系數,其復雜度為O(N^2)

3.3.java實現

package Sort;

import java.util.Arrays;

public class BubbleSort {

    public static void main(String[] args) {
        int[] arr1 = new int[] {1,5,1,3,2,0,-1,2,6,10,20,15,2,8};
        bubbleSort(arr1);
    }
    //自定義冒泡排序的方法
    public static void bubbleSort(int[] arr) {
        for(int i = 0; i<arr.length;i++) {
            for(int j =i+1;j<arr.length;j++) {
                //從第一個數字開始,與後面一個數字比較,若前面的數字>後面的數字,則兩個數字交換位置;反之則不變
                if(arr[i]>arr[j]) {
                    //兩個數據交換,定義一個臨時變量接收其中一個
                    int temp = arr[i];
                    arr[i]=arr[j];
                    arr[j]=temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}

4.快速排序

4.1.基本思想

  快速排序是我們之前學習的冒泡排序的升級,他們都屬於交換類排序,都是采用不斷的比較和移動來實現排序的。快速排序是一種非常高效的排序算法,它的實現,增大了記錄的比較和移動的距離,將關鍵字較大的記錄從前面直接移動到後面,關鍵字較小的記錄從後面直接移動到前面,從而減少了總的比較次數和移動次數。同時采用“分而治之”的思想,把大的拆分為小的,小的拆分為更小的,其原理如下:對於給定的一組記錄,選擇一個基準元素,通常選擇第一個元素或者最後一個元素,通過一趟掃描,將待排序列分成兩部分,一部分比基準元素小,一部分大於等於基準元素,此時基準元素在???排好序後的正確位置,然後再用同樣的方法遞歸地排序劃分的兩部分,直到序列中的所有記錄均有序為止。

4.2.復雜度分析

(1)最壞時間復雜度
  最壞情況是指每次區間劃分的結果都是基準關鍵字的左邊(或右邊)序列為空,而另一邊區間中的記錄僅比排序前少了一項,即選擇的關鍵字是待排序記錄的最小值或最大值。最壞情況下快速排序的時間復雜度為技術分享圖片
(2)最好時間復雜度
  最好情況是指每次區間劃分的結果都是基準關鍵字的左右兩邊長度相等或者相差為1,即選擇的基準關鍵字為待排序的記錄的中間值。此時進行比較次數總共為 nlogn,所以最好情況下快速排序的時間復雜度為技術分享圖片
(3)平均時間復雜度
  快速排序的平均時間復雜度為技術分享圖片。在所有平均時間復雜度為O(nlogn)的算法中,快速排序的平均性能是最好的。
(4)空間復雜度
  快速排序的過程中需要一個棧空間來實現遞歸。最好情況,遞歸樹的深度為技術分享圖片,其空間復雜度也就是O(nlogn);最壞情況下,需要進行 n-1次遞歸,其空間復雜度為O(n);平均情況,空間復雜度為O(nlogn).
(5)基準關鍵字的選取,基準關鍵字的選取是決定快速排序算法的關鍵,常用的基準關鍵字的選取方式如下:
  第一種:三者取中。將序列首、尾和中間位置上的記錄進行比較,選擇三者中值作為基準關鍵字。
  第二種:取left和right之間的一個隨機數技術分享圖片,用n[m]作為基準關鍵字。采用這種方法得到的快速排序一般稱為隨機的快速排序。

4.3.java實現

package Sort;

import java.util.Arrays;

public class QuickSort {

    public static void main(String[] args) {
        long starttime = System.nanoTime();
        int[] arr = new int[] {-85,-91,44,35,-4,-35,48,-7,-25,75,-84,-8,21,-95,97,-84,-76,-28,-7,80,5,89,-51,-81,34,54,-15,29,45,26,83,56,47,-36,-19,39,-9,-63,-82,19,33,27,-1,-65,8,14,-89,-3,-46,25,46,21,100,39,82,-48,-7,2,66,51,94,-23,0,-30,-23,66,24,-32,56,41,-5,-87,56,-28,-36,-73,92,-76,-19,-37,89,-83,8,47,94,-64,53,75,-11,84,44,34,-69,-5,-28,46,-24,20,-43,98,-87,18,70,-98,-75,30,-9,-67,-27,-100,-54,-81,91,73,53,91,-61,52,13,3,-35,-15,-44,-27,22,-97,-17,-73,52,47,70,56,37,-75,-29,-95,-54,-77,-19,70,8,-70,89,-53,76,-7,-48,-47,81,-92,67,58,-58,-26,24,-96,95,-78,95,-18,9,-65,81,-82,40,-72,72,-31,-86,-58,25,5,8,-100,76,-5,62,40,4,41,27,48,-59,75,81,-66,-20,-66,25,8,100,-77,-39,-95,-73,-47,-76,22,-19,37,-36,-92,1,-32,-63,-98,99,-15,72,65,-3,-97,-17,-15,-10,-89,3,10,68,-51,-73,-68,-67,-58,69,11,43,-24,-42,-5,-47,-4,79,-12,58,28,-38,-43,-80,-64,-99,-34,-91,-55,-24,-54,-31,30,46,-10,62,-95,-7,-7,-77,45,82,-34,65,0,6,31,3,9,-89,78,-95,-83,-16,-86,12,76,-68,-30,-76,72,-14,19,-50,-22,88,-22,86,-18,-79,40,-93,-50,-84,29,-92,-10,-63,-51,-96,-21,-24,-21,-4,-12,64,35,13,68,24,24,-54,-32,74,15,-96,42,42,-69,-30,28,10,93,-86,-88,2,-24,99,7,-10,25,68,65,4,-100,-27,6,58,-88,15,-59,19,-99,9,-20,66,79,-2,-100,-98,-27,16,10,60,71,17,-57,-19,23,-1,57,17,72,-46,-69,28,-73,29,-7,-46,84,80,-43,-11,67,-10,-66,-8,-54,69,82,-70,63,75,-22,6,-10,-67,33,97,-86,-20,36,56,-9,84,67,-8,31,55,-13,-32,65,-79,45,-48,33,-28,38,65,23,7,-81,39,-18,-82,70,67,18,92,-12,61,-47,-71,99,-61,-3,-46,68,55,-23,-73,21,82,-96,-89,27,-46,-28,29,-29,-39,83,69,34,-2,-40,-5,-94,8,8,-86,28,-92,25,-43,29,-98,-71,45,74,60,90,3,77,-37,53,-75,-4,85,46,28,-70,-73,-32,53,16,-97,-20,-55,-4,-81,54,-6,15,90,58,-36,34,82,37,-53,100,-18,-42,-55,4,-53,31,100,93,-14,-69,-7,22,-64,-55,-66,33,-46,-80,89,-81,-48,-61,-71,69,64,12,-43,-85,87,-55,82,-28,-20,-64,92,-1,4,-24,20,99,-69,70,-91,72,89,-84,3,-40,-55,57,40,7,-86,1,-26,-22,87,-24,-91,-30,45,-47,35,-58,32,20,-74,-35,-11,73,81,67,-3,80,-97,92,43,89,-69,80,-65,89,68,83,-86,-50,58,75,-18,-37,-72,-84,-28,7,12,-77,-40,-34,-98,83,-49,-79,-77,-86,14,50,-58,68,92,34,68,-49,-98,-13,-67,82,29,83,73,82,-99,90,-23,-54,96,8,-4,11,-88,4,62,-65,35,43,47,46,67,9,-14,16,-29,65,24,83,63,-92,-54,-21,-57,-80,-17,-83,-79,42,91,80,-28,-13,95,-34,-51,-23,-84,43,-21,-74,91,11,-24,-92,-65,-60,7,30,-24,-86,-95,-3,82,-40,-85,9,44,-67,-26,27,-54,80,73,46,57,-64,19,37,94,-74,-89,4,-54,-19,58,37,18,-3,88,-45,-21,16,-45,18,-63,-74,-68,-89,46,62,-14,-41,48,-27,79,-91,23,96,-74,-75,-62,-5,25,-28,-37,8,-40,92,-59,89,95,-41,94,-72,12,-10,-62,72,-50,-11,32,-23,-67,93,-40,11,66,11,-71,-56,85,-88,-56,-37,79,-66,77,-60,-75,35,45,36,-9,-5,-86,42,-52,76,-18,-41,27,-15,-55,74,16,-95,-98,36,-64,63,-96,-96,31,52,67,29,26,-6,83,-74,-72,-17,-31,-58,-73,90,64,14,-87,-95,-44,72,64,-74,-40,73,6,-37,25,-18,73,89,88,85,44,-81,-86,-99,23,60,-19,-33,27,88,-35,42,36,-98,-68,-44,-18,-6,-44,84,-34,34,83,30,90,-35,-62,-54,-92,-22,-59,-46,94,-51,3,-98,-38,-1,-54,47,97,1,25,-96,65,96,-65,74,2,-11,98,80,-43,44,21,23,-9,9,98,-49,-72,-7,20,13,51,3,59,76,-7,95,96,28,-100,38,-17,24,28,-80,-48,-90,30,-29,-84,18,32,-50,86,70,-30,-96,36,89,-33,-91,-82,-78,-29,57,27,-6,-73,-79,-17,-11,-93,-12,92,-26,-71,3,67,88,-80,-68,-44,-82,-28,92,86,3,-22,77,52,-50,-83,50,-38,21,18,-8,-42,6,-10,28,-1,-92,20,0,-60,-44,60,-21,0,95,58,-89,-31,-49,11,88,41,8,73,97,36,1,-39,8,-6,-86,0,-87,75,-86,-36,63,40,-90,-88,54,17,-15,18,22,75,-31,-46,57,37,42,-22,-17,-68,64,-49,97,-60,73,63,-18,-23,-73};
        quickSort(arr, 0, arr.length-1);
        System.out.println(Arrays.toString(arr));
        System.out.println(System.nanoTime()-starttime);
    }
    /**
     * @param arr:傳入的數組
     * @param start:開始排序的數組序號
     * @param end :結束排序的數組序號
     * */
    public static void quickSort(int[] arr,int start,int end) {
        if(start>=end) return;
        //第一個數字作為基準數字
        int left = start;
        int right = end;
        int pivot = arr[(start+end)/2];
        //當左邊<右邊時執行
        while(left<right) {
            //當右邊>基準數
            while(left < right && arr[right]>=pivot) {
                right--;
            }
            arr[left]=arr[right];
            while(left<right && arr[left]<=pivot) {
                left++;
            }
            arr[right]=arr[left];
        }
        //當左標與右標相同,把基準數字賦值給數組的這個序號
        arr[left]=pivot;
        //叠代切割後的兩部分
        quickSort(arr, start, left-1); quickSort(arr, left+1, end);
    }
}

5.選擇排序

5.1.基本思想

  選擇排序是一種簡單直觀的排序算法,其基本原理如下:對於給定的一組記錄,經過第一輪比較後得到最小的記錄,然後將該記錄的位置與第一個記錄的位置交換;接著對不包括第一個記錄以外的其他記錄進行第二次比較,得到最小記錄並與第二個位置記錄交換;重復該過程,知道進行比較的記錄只剩下一個為止。

5.2.復雜度分析

  從簡單選擇排序的過程來看,它最大的特點是交換移動數據次數相當少,這樣就節約了相應的時間。分析它的時間復雜度發現,無論是最好最差情況,其比較次數都是一樣多,第 i 趟排序需要進行 n-i 次關鍵字比較,此時需要比較次技術分享圖片,對於交換次數而言,當最好的時候,交換0次,最差的時候,也就是初始降時,交換次數為 n-1 次,基於最終的時間排序與交換次數總和,因此,總的時間復雜度依然為技術分享圖片
盡管與冒泡排序同為技術分享圖片,但簡單選擇排序的性能要優於冒泡排序。

5.3.java實現

package Sort;

import java.util.Arrays;

public class SelectSort {

    public static void main(String[] args) {
        int[] arr = { 5, 6, 1, 2, 4, 0, 13, 4, 20, 1, 3, 7, 8, 6 };
        selectSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void selectSort(int[] arr) {
        //遍歷所有的數
        for(int i = 0; i<arr.length;i++) {
            int minIndex = i;
            //把當前遍歷的數字和後面所有的數字進行比較,並記錄下最小的數字的下標
            for(int j = i+1;j<arr.length;j++) {
                //如果後面比較的數字比記錄的最小的數還要小,則將minIndex變成最小的數字的下標
                if(arr[minIndex]>arr[j]) {
                    minIndex=j;
                }
            }
            //如果最小的數和當前遍歷的數的下標不一致
            if(i != minIndex) {
                int temp = arr[i];
                arr[i]=arr[minIndex];
                arr[minIndex]=temp;
            }
        }
    }
}

6.堆排序

6.1.基本思想

  堆排序就是利用堆(假設利用大頂堆)進行排序的方法。它的基本思想是,將待排序的序列構造成一個大頂堆。此時,整個序列的最大值就是堆頂的根節點。將它移走(其實就是將其與堆數組的末尾元素交換,此時末尾元素就是最大值),然後將剩余的 n-1 個序列重新構造成一個堆,這樣就會得到 n 個元素中次大的值。如此反復執行,便能得到一個有序序列了。
  堆排序的實現需要解決的兩個關鍵問題:
(1)將一個無序序列構成一個堆。
(2)輸出堆頂元素後,調整剩余元素成為一個新堆。

堆排序的實現:

① 初始化操作:將R[1..n]構造為初始堆;
②每一趟排序的基本操作:將當前無序區的堆頂記錄R[1]和該區間的最後一個記錄交換,然後將新的無序區調整為堆(亦稱重建堆)。

註意:
①只需做n-1趟排序,選出較大的n-1個關鍵字即可以使得文件遞增有序。
②用小根堆排序與利用大根堆類似,只不過其排序結果是遞減有序的。堆排序和直接選擇排序相反:在任何時刻堆排序中無序區總是在有序區之前,且有序區是在原向量的尾部由後往前逐步擴大至整個向量為止。

6.2.復雜度分析

  堆排序的運行時間主要耗費在初始構建堆和在重建堆時反復篩選上。在構建對的過程中,因為我們是完全二叉樹從最下層最右邊的非終端節點開始構建,將它與其孩子進行比較和若有必要的互換,對每個非終端節點來說,其實最多進行兩次比較和互換操作,因此整個構建堆的時間復雜度為O(n)
  在正式排序時,第i次取堆頂記錄重建堆需要用O(logi)的時間(完全二叉樹的某個節點到根節點的距離為技術分享圖片),並且需要取n-1次堆頂記錄,因此,重建堆的時間復雜度為O(nlogn)
  所以總體來說,堆排序的時間復雜度為O(nlogn),由於堆排序對原始記錄的狀態並不敏感,因此它無論是最好、最壞和平均時間復雜度均為O(nlogn)。這在性能上顯然要遠遠好過於冒泡、簡單選擇、直接插入的時間復雜度了。
  空間復雜度上,它只有一個用來交換的暫存單元,也非常的不錯。不過由於記錄的比較與交換是跳躍式進行的,因此堆排序也是一種不穩定的排序方法。
  另外,由於出事構建堆所需要的比較次數比較多,因此,他並不適合待排序序列個數較少的情況。

6.3.java實現

package Sort;

import java.util.Arrays;

public class HeapSort {

    public static void main(String[] args) {

        int[] data = { 1, 3, 6, 2, 4, 8, 9, 5, 12 };

        // 3.選擇排序:堆排序
        System.out.println("堆排序:\t" + Arrays.toString(heapSort(data)));

    }

    private static int[] heapSort(int[] data) {
        data = buildMaxHeap(data); // 初始建堆,array[0]為第一趟值最大的元素
        for (int i = data.length - 1; i > 1; i--) {
            int temp = data[0]; // 將堆頂元素和堆低元素交換,即得到當前最大元素正確的排序位置
            data[0] = data[i];
            data[i] = temp;
            adjustDownToUp(data, 0, i); // 整理,將剩余的元素整理成堆
        }
        return data;
    }

    // 構建大根堆:將array看成完全二叉樹的順序存儲結構
    private static int[] buildMaxHeap(int[] array) {
        // 從最後一個節點array.length-1的父節點(array.length-1-1)/2開始,直到根節點0,反復調整堆
        for (int i = (array.length - 2) / 2; i >= 0; i--) {
            adjustDownToUp(array, i, array.length);
        }
        return array;
    }

    // 將元素array[k]自下往上逐步調整樹形結構
    private static void adjustDownToUp(int[] array, int k, int length) {
        int temp = array[k];
        for (int i = 2 * k + 1; i < length - 1; i = 2 * i + 1) { // i為初始化為節點k的左孩子,沿節點較大的子節點向下調整
            if (i < length && array[i] < array[i + 1]) { // 取節點較大的子節點的下標
                i++; // 如果節點的右孩子>左孩子,則取右孩子節點的下標
            }
            if (temp >= array[i]) { // 根節點 >=左右子女中關鍵字較大者,調整結束
                break;
            } else { // 根節點 <左右子女中關鍵字較大者
                array[k] = array[i]; // 將左右子結點中較大值array[i]調整到雙親節點上
                k = i; // 【關鍵】修改k值,以便繼續向下調整
            }
        }
        array[k] = temp; // 被調整的結點的值放人最終位置
    }

}

7.歸並排序

7.1.基本思想

  歸並排序就是利用歸並的思想實現的排序方法。而且充分利用了完全二叉樹的深度是技術分享圖片的特性,因此效率比較高。其基本原理如下:對於給定的一組記錄,利用遞歸與分治技術將數據序列劃分成為越來越小的半子表,在對半子表排序,最後再用遞歸方法將排好序的半子表合並成為越來越大的有序序列。
  經過第一輪比較後得到最小的記錄,然後將該記錄的位置與第一個記錄的位置交換;接著對不包括第一個記錄以外的其他記錄進行第二次比較,得到最小記錄並與第二個位置記錄交換;重復該過程,知道進行比較的記錄只剩下一個為止。

7.2.復雜度分析

  一趟歸並需要將數組 a[]中相鄰的長度為h的有序序列進行兩兩歸並.並將結果放到temp[]中,這需要將待排序列中的所有記錄掃描一遍,因此耗費O(n),而又完全二叉樹的深度可知,整個歸並排序需要進行(技術分享圖片)次,因此總的時間復雜度為O(nlogn),而且這是歸並排序算法中最好、最壞、平均的時間性能。
由於歸並排序在歸並過程中需要與原始序列同樣數量的存儲空間存放歸並結果以及遞歸時深度為技術分享圖片的棧空間,因此空間復雜度為O(n+logn).
另外,對代碼進行仔細研究,發現merge函數中有if (a[i] < a[j]) 的語句,說明它需要兩兩比較,不存在跳躍,因此歸並排序是一種穩定的排序算法。
也就是說,歸並排序是一種比較占內存,但卻效率高且穩定的算法。

7.3.工作原理

(1)申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合並後的序列
(2)設定兩個指針,最初位置分別為兩個已經排序序列的起始位置
(3)比較兩個指針所指向的元素,選擇相對小的元素放入到合並空間,並移???指針到下一位置
(4)重復步驟3直到某一指針達到序列尾
(5)將另一序列剩下的所有元素直接復制到合並序列尾

7.4.java實現

package Sort.MergeSort;

import java.util.Arrays;

public class MergeSort {

    public static void main(String[] args) {
        int[] arr = {1,3,5,2,4,6,8,1};
        System.out.println(Arrays.toString(arr));
        mergeSort(arr, 0, arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
    public static void merge(int[]arr,int low,int mid,int high) {
        //用於存儲歸並後的臨時數組
        int[] temp = new int[high-low+1];
        //用於記錄第一個數組需要遍歷的下標
        int i = low;
        //用於記錄第二個數組需要遍歷的下標
        int j = mid + 1;
        //用於記錄在臨時數組中存放的
        int index = 0;
        //遍歷兩個數組取出小的數字放入臨時數組
        while(i<=mid&&j<=high) {
            //把小的數據放入臨時數組中,並讓下標向後移1
            if(arr[i]<=arr[j]) temp[index++]=arr[i++];
            else               temp[index++]=arr[j++];
        }
        //處理多余的數據
        while(j<=high) temp[index++]=arr[j++];
        while(i<=mid)  temp[index++]=arr[i++];
        //把臨時數組中的數據重新存入原數組
        for(int k = 0; k<temp.length;k++) arr[k+low]=temp[k];
    }
    public static void mergeSort(int[] arr,int low,int high) {
        int mid = (high+low)/2;
        //遞歸結束條件
        if(low<high) {
        //處理左邊
        mergeSort(arr, low, mid);
        //處理右邊
        mergeSort(arr, mid+1, high);
        //歸並
        merge(arr, low, mid, high);
        }
    }
}

8.基數排序

8.1.基本思想

  像選擇排序、插入排序、快速排序等都是基於兩個元素的比較進行排序的。而基數排序無需進行元素比較,基於隊列處理就能夠達到排序的目的。

  基數排序不是基於排序關鍵字來比較排序項,而是基於排序關鍵字的結構。對於排序關鍵字中的每一個數字或字符的每一種可能取值,都會創建一個單獨的隊列。隊列的數目就稱為基數。

  例如:要排序全部由小寫字母組成的字符串,則基數就是26,就會用到26個單獨的隊列。如果對十進制數進行排序,則基數應該是10.

8.2.復雜度分析

  在基數排序中,沒有任何元素的比較和交換,元素只是在每一輪中從一個隊列移動到另一個隊列。對於給定的基數,遍歷數據的輪次是一個常數,它與排序關鍵字的數目無關,於是,基數排序算法的時間復雜度為O(n)

1.基數排序算法要根據給定問題特別設計;
2.如果排序關鍵字中的數字數目與列表中元素的數目接近,那麽算法的時間復雜度接近O(n平方);
3.基數影響空間復雜度。

8.3.java實現

package Sort;

import java.util.Arrays;

public class RadixSort {
    public static void main(String[] args) {
        int[] arr = new int[] { 123, 55, 451, 313, 22, 0, 145, 2213, 6456, 104, 20, 15, 29, 48 };
        radixSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    private static void radixSort(int[] arr) {
        // 存儲數組中的最大數字
        int max = Integer.MIN_VALUE;//將max值初始為Integer類型的最小值
        //遍歷數組,把最大值賦給max
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > max)
                max = arr[i];
        }
        // 所有數字先加上一個最大數字的絕對數,保證整個數組都是>=0
        // 計算數字是幾位數
        int maxlength = (max + "").length();//(max+"")將最大值轉換成String類型,再得到它的長度
        // 用於臨時存儲數據的數組(二維數組)
        int[][] temp = new int[10][arr.length];
        // 根據最大長度來決定比較次數
        for (int i = 0, n = 1; i < maxlength; i++, n *= 10) {
            int[] count = new int[10];
            // 把每一個數字分別計算余數
            for (int j = 0; j < arr.length; j++) {
                int yushu = arr[j] / n % 10;
                temp[yushu][count[yushu]++] = arr[j];
            }
            //記錄取的元素需要放的位置
            int index = 0;
            //把數字取出來
            for(int k = 0;k<count.length;k++) {
                //記錄數量的數組中,若不為0才取
                if(count[k]!=0) {
                    //取出元素
                    for(int l = 0;l<count[k];l++) {
                        arr[index++]=temp[k][l];
                    }
                    count[k]=0;
                }
            }
        }
    }
}

Java中8種常見的排序方法