數據結構(3)歸併排序

歸併排序

  • 歸併排序原理

  歸併排序(Merging Sort)就是利用歸併的思想實現的排序方法。它的原理是假設初始序列含有n個記錄,則能夠當作是n個有序的子序列,每一個子序列的長度爲1,而後兩兩歸併,獲得[n/2]個長度爲2或1的有序子序列,再兩兩歸併,直至獲得一個長度爲n的有序序列爲止,這種排序方法成爲2路歸併排序。java

  • 先看代碼  
package Sort;

class MergeSort {

	public static void main(String[] args) {
		
		int[] list = {80,40,50,30,60,70,10,90,20};;
		MergeSort ms = new MergeSort(); 
		ms.MSort(list);
		System.out.println("");
		System.out.println("最終結果:");
		ms.printArrry(list);
	}
	

	private void printArrry(int[] a){
		for(int k = 0; k <= a.length-1; k++){
			System.out.print(a[k]);
		}
	}
	private void MSort(int[] list) {
		mergeSort(list, 0, list.length-1);
	}
	
	private void mergeSort(int [] list,int a, int b){

		int mid;
		if (a == b){
			//System.out.println("執行到最底層:");
			//printArrry(list);
			return; 
		}
		else {
			mid = (a+b)/2;
			mergeSort(list, a, mid);
			mergeSort(list, mid+1, b);
			Merge(list, a, mid, b);
			System.out.println("\n"+"left:"+a+" mid:"+mid+" right:"+b);
			System.out.println("此時數組爲:");
			printArrry(list);
		}
	}
	
	private void Merge(int [] list, int left, int mid, int right){
		int a,b,c,d;
		a = left;
		b = mid+1;
		c = left;
		d = left;
		int[] tempaddr = new int[list.length];
		while (a<=mid && b<=right) {
			if (list[a] <= list[b]) {
				tempaddr[c++] = list[a++];
			}
			else {
				tempaddr[c++] = list[b++];
			}
		}
		while (a<=mid) {
			tempaddr[c++] = list[a++];
		}
		while (b<=right) {
			tempaddr[c++] = list[b++];
		}
		while (d <= right){
			list[d] = tempaddr[d++];
		}
	}
}
  • mergeSort()分析
	private void mergeSort(int [] list,int a, int b){
		int mid;
		/*當拆分至最小單元時,則跳出*/
		if (a == b){
			return; 
		}
		else {
			mid = (a+b)/2;
			/*將上一次拆分後的數組list的前半部分再進行拆分*/
			mergeSort(list, a, mid);
			/*將上一次拆分後數組list的後半部分再進行拆分*/
			mergeSort(list, mid+1, b);
			/*將拆分至list最小單元的前半部分和後半部分進行歸併*/
			Merge(list, a, mid, b);
			System.out.println("\n"+"left:"+a+" mid:"+mid+" right:"+b);
			System.out.println("此時數組爲:");
			printArrry(list);
		}
	}
  • Merge()分析  
	private void Merge(int [] list, int left, int mid, int right){
		int a,b,c,d;
		a = left;
		b = mid+1;
		c = left;
		d = left;
		int[] tempaddr = new int[list.length];
		/*將兩個有序的數組,從頭開始比較,較小值存入tempaddr[]*/
		while (a<=mid && b<=right) {
			if (list[a] <= list[b]) {
				tempaddr[c++] = list[a++];
			}
			else {
				tempaddr[c++] = list[b++];
			}
		}
		/*將前半部分還有剩餘的數組取出,存入tempaddr[]*/
		while (a<=mid) {
			tempaddr[c++] = list[a++];
		}
		/*將後半部分還有剩餘的數組取出,存入tempaddr[]*/
		while (b<=right) {
			tempaddr[c++] = list[b++];
		}
		/*將已經歸併好的數組,傳給list,使得list原有的對應於list[left..right]的部分有序*/
		while (d <= right){
			list[d] = tempaddr[d++];
		}
	}
  • 輸出結果  
left:0 mid:0 right:1
此時數組爲:
408050306070109020
left:0 mid:1 right:2
此時數組爲:
405080306070109020
left:3 mid:3 right:4
此時數組爲:
405080306070109020
left:0 mid:2 right:4
此時數組爲:
304050608070109020
left:5 mid:5 right:6
此時數組爲:
304050608010709020
left:7 mid:7 right:8
此時數組爲:
304050608010702090
left:5 mid:6 right:8
此時數組爲:
304050608010207090
left:0 mid:4 right:8
此時數組爲:
102030405060708090
最終結果:
102030405060708090
  • 原理圖示

  紅色部分顯示的是,此時操做的數組的區間,黑色部分,則是未被操做的區間。c++

  經過這張圖能夠看出,由於mid=(a+b)/2,mergeSort(list, a, mid),因此list數組一路向下拆分,從0-8,到0-4,到0-2,到0-1,最後到0-0,此時獲得return,返回遞歸上一層,此時mid爲0,mergeSort(list, mid+1, b),也能夠獲得return,而後就執行Merge(list, a, mid, b)。這句話就是把此時(a,mid,b)=(0,0,1)進行了歸併,使得list[0..1]有順序。數組

  以此類推,接下來依次是(a,mid,b)=(0,1,2),(a,mid,b)=(0,2,4),....,(a,mid,b)=(5,6,8),(a,mid,b)=(0,4,8)spa

  • 時間複雜度分析

將list[1..n]的相鄰長度爲h的有序序列進行兩兩歸併,並將結果放回list[1..n],須要耗時O(N)的時間,由徹底二叉樹的深度剋制,整個排序須要進行log2N次,因此總的時間複雜度爲O(nlogn)。這比上一篇講的冒泡排序,直選排序都要快速,因此應用範圍也更爲普遍。blog

相關文章
相關標籤/搜索