參考文章:
https://www.cnblogs.com/guoyaohua/p/8600214.html 十大經典排序算法最強總結(含JAVA代碼實現)
https://mp.weixin.qq.com/s/gHjWM2eAyA4_kSRt4CMZmQ 歸併排序就這麼簡單
歸併排序分析:
平均時間複雜度 | 最好情況 | 最壞情況 | 空間複雜度 | 排序方式 | 穩定性 |
O(n*logn) | O(n*logn) | O(n*logn) | O(n) | Out-place | 穩定 |
歸併排序原理:
歸併排序是建立在歸併操作上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。歸併排序是一種穩定的排序方法。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲2-路歸併。
歸併排序算法描述:
- 把長度爲n的輸入序列分成兩個長度爲n/2的子序列;
- 對這兩個子序列分別採用歸併排序;
- 將兩個排序好的子序列合併成一個最終的排序序列。
算法原理圖解:
Java代碼實現:
代碼一:
package com.sorting.algorithm;
import java.util.Arrays;
public class MergeSort {
public static int[] mergeSort(int[] array){
if(array.length<2)
return array;
int mid = array.length/2;
int[] left = Arrays.copyOfRange(array, 0, mid);
int[] right = Arrays.copyOfRange(array, mid, array.length);
return merge(mergeSort(left),mergeSort(right));
}
private static int[] merge(int[] left, int[] right) {
int[] result = new int[left.length+right.length];
for (int i=0, j=0, index=0; index < result.length; index++) {
if(i >= left.length){
result[index] = right[j]; j++;
}else if(j >= right.length){
result[index] = left[i]; i++;
}else if(left[i] < right[j]){
result[index] = left[i]; i++;
}else {
result[index] = right[j]; j++;
}
}
return result;
}
public static void printArr(int[] array){
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + ",");
}
System.out.println();
}
public static void main(String[] args) {
int[] array = { 58,26,10,32 };
System.out.println("排序之前:");
printArr(array);
System.out.println("-------------------------------");
System.out.println("排序過程");
int[] newArray = mergeSort(array);
System.out.println("-------------------------------");
System.out.println("排序之後:");
printArr(newArray);
}
}
代碼二:
package com.sorting.algorithm;
public class MergeSort2 {
public static int[] mergeSort(int[] array,int L, int R){
// 如果只剩餘一個元素,即L==R ,或者L>R 就返回
if(L >= R)
return array;
// 找到數組分割的中間數
int M = L+(R-L)/2;
// 對左邊不斷進行拆分
mergeSort(array, L, M);
// 對右邊不斷進行拆分
mergeSort(array, M+1, R);
// 返回兩個有序數組的合併
return merge(array,L,M,R);
}
/*
* 2-路歸併,歸併兩個已經排好序的數組
* L 數組起始部分
* M 數組中間部分
* R 數組結尾部分
*/
private static int[] merge(int[] array, int L, int M, int R) {
printArr(array);
// 左半部分數組
int[] leftArray = new int[M-L+1];
// 右半部分數組
int[] rightArray = new int[R-M];
// 將原數組左部分拷貝到左半部分數組中
for (int i = L; i <= M; i++) {
leftArray[i-L] = array[i];
}
// 將原數組右部分拷貝到右半部分數組中
for (int j = M+1; j <= R; j++) {
rightArray[j-M-1] = array[j];
}
/*
* i 爲指向 leftArray 數組的模擬指針
* j 爲指向 rightArray 數組的模擬指針
* k 爲指向 array 數組的模擬指針
*/
int i=0,j=0,k=L;
// 比較左右兩部分數組,哪個比較小,就放到原數組中
while(i < leftArray.length && j < rightArray.length){
if(leftArray[i] < rightArray[j]){
array[k] = leftArray[i];
k++;
i++;
}else{
array[k] = rightArray[j];
k++;
j++;
}
}
// 如果左半部分數組有剩餘,就全部拷貝到原數組中
while(i < leftArray.length){
array[k] = leftArray[i];
k++;
i++;
}
// 如果右半部分數組有剩餘,就全部拷貝到原數組中
while(j < rightArray.length){
array[k] = rightArray[j];
k++;
j++;
}
return array;
}
public static void printArr(int[] array){
for (int i = 0; i < array.length; i++) {
if(i != array.length-1)
System.out.print(array[i] + ",");
else
System.out.println(array[i]);
}
System.out.println();
}
public static void main(String[] args) {
int[] array = { 26,58,10,32,11,22,34,60 };
System.out.println("排序之前:");
printArr(array);
System.out.println("-------------------------------");
System.out.println("排序過程");
int[] newArray = mergeSort(array,0,array.length-1);
System.out.println("-------------------------------");
System.out.println("排序之後:");
printArr(newArray);
}
}
代碼三:
package com.sorting.algorithm;
import java.util.Arrays;
public class MergeSort3 {
public static void mergeSort(int[] array, int l, int r) {
// 如果左部分只剩一個數,就返回
if(l >= r)
return ;
// 找到分隔數
int mid = (r+l)/2;
// 分隔左半部分,並繼續分隔,直到最後剩一個數
mergeSort(array, l, mid);
// 分隔右半部分,並繼續分隔,直到最後只剩下一個數
mergeSort(array, mid+1, r);
// 2-路歸併,合併兩個有序的數組
merge(array,l,mid,r);
}
/*
* 2-路歸併
* 合併兩個有序的數組,
* array: 是原數組
* l : 是分隔數組中的左起點
* mid : 是分隔左右的分隔下標
* r : 是分隔數組中的右終點
*/
private static void merge(int[] array, int l, int mid, int r) {
// 輔助數組
int[] aux = Arrays.copyOfRange(array, l, r+1);
// i 指向左半部分的起始 , j 指向右半部分的起始
int i=l, j=mid+1;
for (int k = l; k <= r; k++) {
if(i > mid){ // 如果左半部分已經添加完
array[k] = aux[j-l]; j++;
}
else if(j > r){ // 右半部分已經添加完
array[k] = aux[i-l]; i++;
}
else if(aux[i-l] <= aux[j-l]){ // 如果左半部分小於右半部分的數字
array[k] = aux[i-l]; i++;
}
else { // 如果右半部分的數字小於左半部分的數字
array[k] = aux[j-l]; j++;
}
}
}
public static void printArr(int[] array){
for (int i = 0; i < array.length; i++) {
if(i != array.length-1)
System.out.print(array[i] + ",");
else
System.out.println(array[i]);
}
System.out.println();
}
public static void main(String[] args) {
int[] array = { 58,36,70,22,88,64,1,32 };
System.out.println("排序之前:");
printArr(array);
System.out.println("-------------------------------");
System.out.println("排序過程");
mergeSort(array,0,array.length-1);
System.out.println("-------------------------------");
System.out.println("排序之後:");
printArr(array);
}
}
歸併排序測試
代碼一:
代碼二:
代碼三: