十六、自己動手實現排序算法(4)--------- “ Merge Sort 歸併排序 ”

參考文章:

https://www.cnblogs.com/guoyaohua/p/8600214.html                  十大經典排序算法最強總結(含JAVA代碼實現)

https://mp.weixin.qq.com/s/gHjWM2eAyA4_kSRt4CMZmQ          歸併排序就這麼簡單

https://github.com/liuyubobobo/Play-with-Algorithms/blob/master/03-Sorting-Advance/Course%20Code%20(Java)/02-Merge-Sort/src/bobo/algo/MergeSort.java


歸併排序分析:

平均時間複雜度 最好情況 最壞情況 空間複雜度 排序方式 穩定性
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);
		
	}
	
}

 

 

歸併排序測試

代碼一:

 

代碼二:

 

代碼三:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章