《排序算法系列6》歸併排序

1 思想算法

歸併排序(MERGE-SORT)是利用歸併的思想實現的排序方法,該算法採用經典的分治(divide-and-conquer)策略(分治法將問題(divide)成一些小的問題而後遞歸求解,而治(conquer)的階段則將分的階段獲得的各答案"修補"在一塊兒,即分而治之)。數組

2 舉例dom

能夠看到這種結構很像一棵徹底二叉樹,本文的歸併排序咱們採用遞歸去實現(也可採用迭代的方式去實現)。分階段能夠理解爲就是遞歸拆分子序列的過程,遞歸深度爲log2n。ide

合併相鄰有序子序列函數

再來看看階段,咱們須要將兩個已經有序的子序列合併成一個有序序列,好比上圖中的最後一次合併,要將[4,5,7,8]和[1,2,3,6]兩個已經有序的子序列,合併爲最終序列[1,2,3,4,5,6,7,8],來看下實現步驟。測試

歸併排序動態演示spa

 

3 代碼實現code

 1     public static void main(String[] args) {
 2         int[] arr = { 8, 4, 5, 7, 1, 3, 6, 2 };
 3         int[] temp = new int[arr.length];// 歸併排序須要一個額外的空間
 4         mergeSort(arr, 0, arr.length - 1, temp);
 5         System.out.println("歸併排序後的結果" + Arrays.toString(arr));
 6     }
 7     
 8     /**
 9        *  經過遞歸實現數組的分階段  而後調用數組合並方法
10       * 從而實現歸併排序的分而治之思想
11      * @param arr   須要排序的數組
12      * @param left  排序數組的開始索引
13      * @param right 排序數組的結束索引
14      * @param temp  輔助數組,用於接受排序的結果
15      * void
16      */
17     public static void mergeSort(int[] arr, int left, int right, int[] temp) {
18         if (left < right) {
19             int mid = (left + right) / 2;// 中間索引
20             // 向左遞歸進行分解
21             mergeSort(arr, left, mid, temp);
22             // 向右遞歸進行分解
23             mergeSort(arr, mid + 1, right, temp);
24             // 合併
25             merge(arr, left, mid, right, temp);
26         }
27     }
28 
29     /**
30      *  治階段: 將分開的2個數組合並s 
31      * 
32      * @param arr   排序的原始數組
33      * @param left  左邊有序數組
34      * @param mid   中間索引
35      * @param right 右邊索引
36      * @param temp  中轉的數組
37      */
38     public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
39         int i = left;// 左邊有序序列的初始索引
40         int j = mid + 1;// 右邊有序序列的初始索引
41         int t = 0; // 指向temp數組的當前索引
42 
43         // 步驟一:
44         // 先把左右兩邊的數據按照規則填充到temp數組
45         // 直接左右兩邊的數組 有一邊處理完畢爲止
46         while (i <= mid && j <= right) {
47             // 若是左邊的有序序列的當前元素 小於等於右邊有序序列的當前元素
48             // 把左邊的元素添加到temp數組
49             if (arr[i] <= arr[j]) {
50                 temp[t] = arr[i];
51                 i++;
52                 t++;
53             } else {
54                 temp[t] = arr[j];
55                 j++;
56                 t++;
57             }
58         }
59 
60         // 步驟二:
61         // 把有剩餘的數組一邊的數據依次填充到temp
62         while (i <= mid) {
63             temp[t] = arr[i];
64             t++;
65             i++;
66         }
67         while (j <= right) {
68             temp[t] = arr[j];
69             t++;
70             j++;
71         }
72 
73         // 步驟三:
74         // 把temp拷貝到arr
75         //初始化t 
76         t = 0;
77         int tempLeft = left;
78         // 注意 並非每次都拷貝全部 由於不須要拷貝temp 只須要拷貝當前的排好序的數組
79         while (tempLeft <= right) {
80             arr[tempLeft] = temp[t];
81             t++;
82             tempLeft++;
83         }
84     }

4 時間複雜度orm

假如n個元素使用歸併排序的時間複雜度爲T(n),那麼因爲歸併排序使用的是分治思想,T(n)=2*T(n/2)+n,其中n就是兩個子區間合併的時間複雜度,這個從合併函數能夠看出來。能夠推導出如下公式:blog

                        T(1) = C;   n=1 時,只須要常量級的執行時間,因此表示爲 C。
                         T(n) = 2*T(n/2) + n; n>1

 

通過進一步推導,能夠獲得T(n)=2^k * T(n/2^k) + k * n,咱們假設T(1)=T(n/2^k),也就是說當n/2^k個元素的進行歸併排序,達到遞歸終止條件時,n/2^k=1,獲得:k=logn

因而:

                                             T(n)=Cn+nlog2n

歸併排序的時間複雜度就是O(nlogn),跟數組的有序度其實並無什麼關係,是很是穩定的時間複雜度。

 5 時間複雜度速度測試

 1   // 歸併排序
 2     public static void main(String[] args) {
 3         speedTest(8000000);
 4     }
 5 
 6     /**
 7        * 建立一個隨機數組 而後調用排序方法 獲得時間
 8      * 
 9      * @param number 建立的隨機數組個數
10      */
11     public static void speedTest(int number) {
12         int[] arr = new int[number];
13         int[] arr2 = new int[number];
14         for (int i = 0; i < arr.length; i++) {
15             arr[i] = (int) (Math.random() * 800000);
16         }
17 
18         SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
19         Date date1 = new Date();
20         String time1 = simpleDateFormat.format(date1);
21         System.out.println("排序前的時間爲:" + time1);
22 
23         // 調用上面的歸併排序方法
24         mergeSort(arr, 0, arr.length-1, arr2);
25 
26         Date date2 = new Date();
27         String time2 = simpleDateFormat.format(date2);
28         System.out.println("排序後的時間爲:" + time2);
29     }

歸併排序速度測試結果

8萬個數據測試結果大約須要不到1秒

 80萬個數據測試結果大約須要不到

 800萬個數據測試結果大約須要2秒

8000萬個數據測試結果大約16秒

相關文章
相關標籤/搜索