1. 程式人生 > >Java實現--歸併排序(遞迴)

Java實現--歸併排序(遞迴)

《Java資料結構和演算法》如此描述分治演算法:

把一個大問題分成兩個相對來說更小的問題,並且分別解決每一個小問題,對每一個小問題的解決方案是一樣的:把每個小問題分成兩個更小的問題,並且解決它們。這個過程一直持續小去知道達到易於求解的基值情況,就不用再繼續分了。

時間複雜度:O(N*log2N)

首先我們是先歸併兩個有序陣列,

歸併流程如下:

程式碼如下:

/**
 * 歸併演算法
 * @author Administrator
 *
 */
public class MergeApp {
	public static void main(String[] args) {
		int[] arrayA = {22,32,45,79};
		int[] arrayB = {7,14,28,35,88,93};
		int[] arrayC = new int[10];
		
		merge(arrayA,4,arrayB,6,arrayC);
		display(arrayC,10);
	}
	
	public static void merge(int[] arrayA,int sizeA,int[] arrayB,int sizeB,int[] arrayC) {
		int aDex = 0,bDex = 0,cDex = 0;
		while(aDex<sizeA && bDex<sizeB) {
			if(arrayA[aDex]<arrayB[bDex]) 
				arrayC[cDex++] = arrayA[aDex++];
			else
				arrayC[cDex++] = arrayB[bDex++];
		}
		
		while(aDex<sizeA)
			arrayC[cDex++] = arrayA[aDex++];
		while(bDex<sizeB)
			arrayC[cDex++] = arrayB[bDex++];
	}
	
	public static void display(int[] arrayC,int sizeC) {
		for(int i=0;i<sizeC;i++) {
			System.out.print(arrayC[i]+" ");
		}
		System.out.println();
	}
	
}

 

進入正題:

我們將陣列拆成兩個,兩個又分別拆成兩個成了四個,四個又分別拆成兩個,一共成了八個...這樣以此類推

(以左半部分)

  

繼續拆:

這已經拆到lowerBound==upperBound的時候了,這時候我們就合併紅色和綠色的,又按照這個過程從下往上順回去

怎麼合併呢?

我們前面已經提過了如何將兩個有序數組合併到一個數組裡面了對不對?我們剛剛把一個好好的亂序陣列一直拆分一直拆分下去,一直到只有一個數據元素的時候不再進行拆分,我們想當然的也把它們稱之為陣列(方便跟前面的如何將兩個有序數組合並起來),想想看只有一個數據元素的陣列,是不是相當於是個有序陣列呢?因為它只有一個數據元素啊,不用排序說明它已經排序好了,然後我們將兩個已經排序好的陣列(只有一個數據元素的陣列)按照前面“將兩個有序陣列進行合併

”的方法,放到名為workSpace陣列(一個空陣列)裡面,合併完成以後不就有了一個擁有兩個資料元素的有序陣列了嗎?然後我們又跟其它的陣列比較,比如這裡的紅紅綠綠合併之後,再跟黃黃合併接著放到workSpace裡面,然後一直往上推,我們來到將一個數組一分為二的合併,是不是感到很熟悉,是的,這樣的合併我們之前講到了。瞧,是不是每一步都是這樣的,本質就是兩有序合併,兩有序數組合並...

 

 

理解拆分陣列如圖所示:(本質還是同一個陣列)【理解用於merge()方法中】

我們將這個陣列拆成兩半來看,一半是由6 1 8構成,另一半由10 9 7構成,但他們本質上還是在一個數組裡面的,並沒有達到真正的拆,我們在抽象的拆,其運用的操作就是得到不同的小標,如6 1 8 我們如何得到它,只需得到其下標範圍0~2即可,同理得到下標3~5,這樣我們就相當於將一個數組拆成了兩個陣列。

友情提示:結合程式碼和註釋更容易理解哦!


public class NewDarray {
	
	private int[] theArray;
	private int nElems;
	
	public NewDarray(int maxSize) {
		theArray = new int[maxSize];
		nElems = 0;
	}
	
	//往陣列插入元素
	public void insert(int num) {
		theArray[nElems++] = num;
	}
	
	//列印陣列元素
	public void display() {
		for(int i=0;i<nElems;i++)
			System.out.print(theArray[i]+" ");
		System.out.println();
	}
	
	//呼叫排序
	public void mergeSort() {
		int[] workSpace = new int[nElems];
		reMergeSort(workSpace,0,nElems-1);
	}
	
	public void reMergeSort(int[] workSpace,int lowerBound,int upperBound){
		
		//獲取中間下標
		int mid = (lowerBound+upperBound)/2;
		
		//當lowerBound域upperBound相等的時候表示其只有一個數據元素並不需要排列,返回
		if(lowerBound==upperBound)
			return;
		
		//排左邊一半
		reMergeSort(workSpace,lowerBound,mid);
		
		//排右邊
		reMergeSort(workSpace,mid+1,upperBound);
		
		//將兩個有序陣列進行合併		
		merge(workSpace,lowerBound,mid+1,upperBound);
	}
	
	public void merge(int[] workSpace,int lowerPtr,int higherPtr,int upperBound) {
		//我們是將元素排序好以後再複製到theArray(原來的陣列身上),所以我們需要獲取其元素個數n
		//我們還需要複製的起始位置即lowerBound
		//還需要獲取中間下標,使左半部分得到排序
		int j=0;
		int lowerBound = lowerPtr;//獲取起始下標
		int n = upperBound-lowerBound+1;//獲取元素個數
		int mid = higherPtr-1;//獲取中間元素
		
		//在前面已經有如何將兩個有序數組合並,這是同一個道理
		//我們是將theArray拆分成兩個陣列來進行合併的,兩個分別又可以拆為兩個成四個,四個又可以拆...
		//合併都是同一個道理
		
		//兩個"陣列"(就是拆分過的陣列,其本質上還是用一個數組)元素
		while(lowerPtr<=mid && higherPtr<=upperBound)
			if(theArray[lowerPtr]<=theArray[higherPtr])
				workSpace[j++] = theArray[lowerPtr++];
			else
				workSpace[j++] = theArray[higherPtr++];
		
		//將剩下的放進workSpace陣列中
		while(lowerPtr<=mid)
			workSpace[j++] = theArray[lowerPtr++];
		while(higherPtr<=upperBound)
			workSpace[j++] = theArray[higherPtr++];
		
		//將其複製到陣列theArrat中,完成歸併
		for(j=0;j<n;j++)
			theArray[lowerBound+j] = workSpace[j];
	}
	
}