歸併排序(MERGE-SORT)是建立在歸併操作上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併。
白話說:所謂歸併排序,望文生義,就是把兩個東西合併在一起了,那麼是什麼東西合併在一起了呢?是兩個已經排好序的數組進行合併到一個數組中,這兩個排好序的數組分別有一個指針指向第0個元素,誰小誰放進大數組中。
大致排序過程是這樣的:
圖摘自–https://www.cnblogs.com/qingmingsang/articles/5283870.html
先把數組中的元素兩兩比較,創建一個輔助數組,將排好序的數字放進輔助數組中,然後再把輔助數組中的數放回原數組,然後兩個合併好的數組再合併,再創建一個輔助數組,再把兩個數組中排好序的數字放進輔助數組中,再把輔助數組放回原數組中,直到所有的數組全部排好序。
看一下代碼:
package com.bean.com.bean.sortAlg;
import java.util.Arrays;
public class MargeSort {
public static void MergeSort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
MergeSort(arr, 0, arr.length - 1);
}
public static void MergeSort(int[] arr, int left, int right) {
if(left == right) {
return;
}
int mid = left + ((right - left)>>1);//等於(left+right)/2,位運算比較快
MergeSort(arr, left, mid);//把左邊的元素全拆開
MergeSort(arr, mid+1, right);//把右邊的元素全拆開
MergeSort(arr, left, mid, right);//left~right之間排序
}
public static void MergeSort(int[] arr, int left, int mid, int right) {
int[] help = new int[right - left + 1];//輔助數組,暫存排好序的元素
int index = 0;
int p1 = left;//左指針
int p2 = mid + 1;//右指針
while(p1 <= mid && p2 <= right) {//只要左右指針沒超出範圍,就排序
help[index++] = arr[p1] > arr[p2] ? arr[p2++] : arr[p1++];
//誰小誰先入help數組
}
while(p2 <= right) {//p1排完了,p2還有沒排完的元素,則依次入數組
help[index++] = arr[p2++];
}
while(p1 <= mid) {//p2排完了,p1繼續依次扔進help數組
help[index++] = arr[p1++];
}
for(int i = 0; i < help.length; i++) {
arr[left + i] = help[i];//把排好序的元素放回原數組中!
}
}
//打印數組
public static void printArray(int[] arr) {
if(arr == null) {
return;
}
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
//形成隨機數組
public static int[] generateRandomArray(int maxSize, int maxNum) {
if(maxSize <= 0) {
return null;
}
int[] ranArray = new int[(int) (Math.random() * (maxSize + 1) )];
for(int i = 0; i < ranArray.length; i++) {
ranArray[i] = (int) (Math.random() * (maxNum + 1)) - (int) (Math.random() * maxNum);
}
return ranArray;
}
//判斷兩個數組是否相等
public static boolean isEquals(int[] arr1, int[] arr2) {
if(arr1.length != arr2.length) {
return false;
}
if(arr1 == null && arr2 != null || arr1 != null && arr2 == null) {
return false;
}
if(arr1 == null && arr2 == null) {
return true;
}
for(int i = 0; i < arr1.length; i++) {
if(arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
//完全正確的排序方法
public static void comparator(int[] arr) {
Arrays.sort(arr);
}
//複製數組
public static int[] copyArray(int[] arr) {
int[] newArr = new int[arr.length];
for(int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
return newArr;
}
public static void main(String[] args) {
int testTimes = 100000;
int maxNum = 100;
int maxSize = 2000;
boolean flag = true;
for(int i = 0; i < testTimes; i++) {
int[] arr1 = generateRandomArray(maxSize, maxNum);
int[] arr2 = copyArray(arr1);
comparator(arr1);
MergeSort(arr2);
if(!isEquals(arr1, arr2)){
flag = false;
break;
}
}
System.out.println(flag ? "Nice! Nothing is wrong~" : "Ooops! Your method is wrong..");
int[] arr1 = generateRandomArray(maxSize, maxNum);
System.out.print("before:");printArray(arr1);
long beforetime = System.currentTimeMillis();
MergeSort(arr1);
long aftertime = System.currentTimeMillis();
System.out.println("execute time is:"+(aftertime - beforetime)+"ms");
System.out.print("after");printArray(arr1);
}
}
其實就是把一個數組進行拆塊,每一小塊都排序,然後再把兩個排好序的小塊整合,再進行排序,直到所有小塊都排好序,整體排序結束。
時間複雜度
假如有N個元素,那麼每次二分,它的時間複雜度是logN,但是每次二分完了還需要進行比較,比較的次數跟有多少個元素N有關,所以整體時間複雜度是O(n*logn),額外空間複雜度說的就是那個輔助數組,它每次創建完又被銷燬了,所以額外的空間複雜度是O(n),但是根據內部緩存法,可以使額外空間複雜度變成O(1),比較難,所以本篇博客不作記載,如有需要請自行百度。