归并排序
归并算法的中心是归并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)
可以看到归并排序比插入排序要快,比表排序要慢,缺点和表排序相同,都需额外的存储空间