归并排序

归并算法的中心是归并2个已经有序的数组,归并2个有序数组A和B,就生成了一个新的数组C,数组C中包含数组A和B中的所有数据项,并使它们有序的排列在数组C中

不要求数组A和B的长度相同

比较数组A和数组B中的数据项并将较小项首先移动至数组C中

public class Merge {
	private int[] A = {2,6,7};
	private int[] B = {1,3,4,5,8,9,10};
	private int[] C = new int[10];
	
	public void sort(){
		int a = 0;
		int b = 0;
		int c = 0;
		while(a < A.length && b < B.length){
			if(A[a] < B[b])
				C[c++] = A[a++];
			else
				C[c++] = B[b++];
		}
		while(a < A.length)
			C[c++] = A[a++];
		while(b < B.length)
			C[c++] = B[b++];
	}
	
	public void display(){
		for(int i = 0 ; i < C.length ; i++){
			System.out.print(C[i] + " ");
		}
		System.out.print("\n");
	}
	
	public static void main(String[] args) {
		Merge m = new Merge();
		m.sort();
		m.display();
	}
}
1 2 3 4 5 6 7 8 9 10 

归并排序的思想是把一个数组分成两半,排序每一半,再把两半归并成一个有序的数组

如何为每一半排序?把每一半分成两个四分之一,对每个四分之一排序,然后把它们归并成一个有序的一半

依此类推,直到分割出的子数组中只包含一个数据项

public class MergeSort {
	private int[] data;
	
	public MergeSort(int[] data){
		this.data = data;
	}
	
	public void sort(){
		int[] temp = new int[data.length];
		this.sort(temp, 0, data.length - 1);
	}
	
	private void sort(int[] ints,int first,int last){
		if(first == last)
			return;
		int middle = (last + first)/2;
		this.sort(ints, first, middle);
		this.sort(ints, middle + 1, last);
		this.merge(ints, first, middle , last);
	}
	
	private void merge(int[] ints,int first,int middle,
			int last){
		System.out.println(first+" "+last);
		int i = first;
		int a = first;
		int b = middle  + 1;
		while(a <= middle && b <= last){
			if(data[a] < data[b])
				ints[i++] = data[a++];
			else
				ints[i++] = data[b++];
		}
		while(a <= middle)
			ints[i++] = data[a++];
		while(b <= last)
			ints[i++] = data[b++];
		for(i = first ; i < last + 1 ; i++){
			data[i] = ints[i];
		}
	}
	
	public void display(){
		for(int i = 0 ; i < data.length ; i++){
			System.out.print(data[i] + " ");
		}
		System.out.print("\n");
	}
	
	public static void main(String[] args) {
		int[] a = {8, 6, 1, 4, 2, 7, 3, 5};
		int[] b = {6, 5, 1, 4, 2, 7, 3};
		MergeSort ms1 = new MergeSort(a);
		MergeSort ms2 = new MergeSort(b);
		ms1.sort();
		ms1.display();
		ms2.sort();
		ms2.display();
	}
}
0 1
2 3
0 3
4 5
6 7
4 7
0 7
1 2 4 6 7 3 5 8 
0 1
2 3
0 3
4 5
4 6
0 6
1 2 3 4 5 6 7  

ms1的归并过程如下图:

ms2的归并过程如下图:

归并排序的效率:

数据移动次数:

数据移动只会发生在归并过程中,通过ms1的归并过程图,如果数组长度为8,数据总共需要进行2+2+2+2+4+4+8=24次移动,等于8*log28,考虑到每次归并完成时,需将归并结果按顺序拷贝回原数组(从数组ints拷贝回数组data),故实际移动次数为24*2=48次

如果数据长度为7,通过ms2的归并规程图,数据总共需要进行(2+2+2+4+3+7)*2=40次移动

同理,如果数据长度为6,数据需进行次移动(2+2+3+3+6)*2=32

如果数据长度为5,数据需进行(2+2+3+5)*2=24次移动

如果数据长度为4,数据需进行(2+2+4)*2=16次移动

综上,数据移动的时间复杂度约为2*N*log2N,即O(N*logN)

数据比较次数:

由于在每次归并过程中都需要进行比较,将2个长度为2的数组归并为一个长度为4的数组,最少需要比较2次,即其中一个长度为2的数组所包含的所有数据项都比另一个长度为2的数组所包含的数据项要小,最多需要比较4-1=3次,因此,将2个数组归并为一个长度为N的数组,大约需要比较(N/2+N-1)/2次

对于长度为8的数组,大约需要进行(2/2+2-1)/2+(2/2+2-1)/2+(2/2+2-1)/2+(2/2+2-1)/2+(4/2+4-1)/2+(4/2+4-1)/2+(8/2+8-1)/2=14.5次比较

对于长度为7的数组,大约需要进行(2/2+2-1)/2+(2/2+2-1)/2+(2/2+2-1)/2+(4/2+4-1)/2+(3/2+3-1)/2+(7/2+7-1)/2=10.75

同理,对于长度为6的数组,大约需要进行9.5次比较

对于长度为5的数组,大约需要进行7次比较

对于长度为4的数组,大约需要进行4.5次比较

综上,数据比较次数肯定是原远小于数据移动次数的

故总的时间复杂度为O(N*logN)

可以看到归并排序比插入排序要快,比表排序要慢,缺点和表排序相同,都需额外的存储空间

 

posted @ 2014-04-13 22:42  心意合一  阅读(198)  评论(0编辑  收藏  举报