1. 程式人生 > >分治歸併排序演算法——Java實現

分治歸併排序演算法——Java實現

1、分治法

    許多有用的演算法在結構上是遞迴的:為了解決一個給定的問題,演算法一次或多次遞迴地呼叫其自身以解決緊密相關的若干子問題。這些演算法典型地遵循分治法的思想:將原問題分解為幾個規模較小但類似於原問題的子問題,遞迴地求解這些子問題,然後再合併這些子問題的解來建立原問題的解。

    分治模式在每層遞迴時都有三個步驟:

   (1)分解原問題為若干子問題,這些子問題是原問題的規模較小的例項。

   (2)解決這些子問題,遞迴地求解各子問題。然而,若子問題的規模足夠小,則直接求解。

   (3)合併這些子問題的解成原問題的解。

2、歸併排序演算法

    歸併排序演算法完全遵循分治模式。直觀上其操作如下:

   (1)分解:分解待排序的n個元素的序列成各具n/2個元素的兩個子序列。

   (2)解決:使用歸併排序遞迴地排序兩個子序列。

   (3)合併:合併兩個已排序的子序列以產生已排序的答案。

    當待排序的序列長度為1時,遞迴“開始回升”,在這種情況下不要做任何工作,因為長度為1的每個序列都已排好序。歸併排序演算法的關鍵操作是“合併”步驟中兩個已排序序列的合併。我們通過呼叫一個輔助過程Merge(A,p,q,r)來完成合並,其中A是一個數組,p、q和r是陣列下標,滿足p<=q<r。該過程假設子陣列A[p,q]和A[q+1,r]都已排好序。它合併這兩個子陣列形成單一的已排好序的子陣列並代替當前的子陣列A[p,r]。

    過程Merge按以下方式工作。回到我們玩撲克牌的例子,假設桌上有兩堆牌面朝上的牌,每堆都已排序,最小的牌在頂上。我們希望把這兩堆牌合併成單一的排好序的輸出堆,牌面朝下地放在桌上。我們的基本步驟包括在牌面朝上的兩堆牌的頂上兩張牌中選取較小的一張,將該牌從其堆中移開(該堆的頂上將顯露一張新牌)並牌面朝下地將該牌放置到輸出堆。

    下面是Merge的虛擬碼:

Merge(A,p,q,r):
tmp[1,..,r-p+1]
i = p
j = q+1
while i <= q && j <= r			
   if A[i] <= A[j]				
	tmp[k++] = A[i++];			
   else			
	tmp[k++] = A[j++];							
   while i <= q			
	tmp[k++] = A[i++];				
   while j <= r			
	tmp[k++] = A[j++];				
   for k2 = 0 to tmp.length			
	A[k2+p] = tmp[k2]; 


    現在我們可以把過程Merge作為歸併排序演算法中的一個子程式來用。下面的過程Merge_sort(A,p,r)排序子陣列A[p,r]中的元素。若p>=r,則該子陣列最多有一個元素,所以已經排好序。否則,分解步驟簡單地計算一個下標q,將A[p,r]分成兩個子陣列A[p,q]和A[q+1,r],前者包含[n/2]個元素,後者包含[n/2]個元素。

Merge_sort(A,p,r):
if p < r
     q = (p+r)/2
     Merge_sort(A,p,q)
     Merge_sort(A,q+1,r)
     Merge(A,p,q,r)
    為了排序整個序列A=(A[0],A[1],...,A[n]),我們執行初始呼叫Merge_sort(A,0,A.length),這裡再次有A.length = n。圖2-4自底向上地說明了當n為2的冪時該過程的操作。演算法由以下操作組成:合併只含1項的序列對形成長度為2的排好序的序列,合併長度為2的序列對形成長度為4的排好序的序列,依此下去,直到長度為n/2的兩個序列被合併最終形成長度為n的排好序的序列。


3、Java程式碼實現

public class Merge_sort_test {
	
	public static void Merge(int[] A,int p,int q,int r){
		
		int[] tmp = new int[r-p+1];//宣告一個臨時陣列,長度為要歸併陣列的長度
		int i = p;   //記住左邊陣列第一個元素的下標
		int j = q+1; //記住右邊陣列第一個元素的下標
		int k = 0;		
		while(i <= q && j <= r){
			//左邊陣列元素和右邊陣列元素比較,把小的元素賦給臨時陣列
			if(A[i] <= A[j]){				
				tmp[k++] = A[i++];
			}
			else{				
				tmp[k++] = A[j++];
			}
		}
		//把左邊剩餘的陣列元素賦給臨時陣列
		while(i <= q){			
			tmp[k++] = A[i++];
		}
		//把右邊剩餘的陣列元素賦給臨時陣列
		while(j <= r){			
			tmp[k++] = A[j++];
		}
		//用臨時陣列元素覆蓋原陣列元素
		for(int k2 = 0;k2 < tmp.length;k2++){			
			A[k2+p] = tmp[k2]; 
		}
	}
	public static void/*int[]*/ Merge_sort(int[] A,int p,int r){
		
		int q = (p+r)/2;
		if(p < r){
			//遞迴呼叫			
			Merge_sort(A,p,q);
			Merge_sort(A,q + 1,r);
			//歸併排序資料元素
			Merge(A,p,q,r);
		}
		//return A;
	}
	public static void main(String[] args) {
		// 
		int[] A = {5,2,4,7,1,3,2,6};
		System.out.println("原始資料: ");
		for(int i = 0;i < A.length;i++){
			
			System.out.print(A[i] + " ");
		}
		System.out.println();
		Merge_sort(A,0,A.length -1);
		System.out.println("輸出結果: ");
		for(int i = 0;i < A.length;i++){
			
			System.out.print(A[i] + " ");
		}
	}

}