归并排序
- 归并排序原理
归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或1的有序子序列,再两两归并,直至得到一个长度为n的有序序列为止,这种排序方法成为2路归并排序。
- 先看代码
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(" "+"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(" "+"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
- 原理图示
红色部分显示的是,此时操作的数组的区间,黑色部分,则是未被操作的区间。
通过这张图可以看出,因为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)
- 时间复杂度分析
将list[1..n]的相邻长度为h的有序序列进行两两归并,并将结果放回list[1..n],需要耗时O(N)的时间,由完全二叉树的深度克制,整个排序需要进行log2N次,所以总的时间复杂度为O(nlogn)。这比上一篇讲的冒泡排序,直选排序都要快速,所以应用范围也更为广泛。