生活日用算法——归并排序的java实现
最近在看sort在1.7下的源码,发现在排序元素少于32的时候,使用的是Meger sort优化版——TimSort。本着溯本求源的精神,我先去看了下归并排序在JAVA的实现,虽然一直以来对归并排序的原理清楚的,但不曾自己写过。网上找了别人实现好的代码,然后自己理解后填上了注释,记录如下以供日后回顾(此代码的尾递归理论上可以转为迭代,以减少空间使用,防止栈的溢出)
首先,这里有一个基础的merge算法,实现对两个有序数组的合并。
1 /** 2 * 将两个有序数组合并为一个(递增) 此处略微取巧,两个有序数组分别为a[beginFrist,beginSecond)与[beginSecond,endSecond] 3 * @param a 指定数组A 4 * @param beginFrist 第一个有序数组的开始位置 5 * @param beginSecond 第一个有序数组的结束位置(第二个有序数组的开始位置) 6 * @param endSecond 第二个有序数组的结束为止 7 */ 8 private static void merge(int[] a, int beginFrist, int beginSecond, int endSecond) { 9 //建立一个中间数组,用于存放这两个有序数组合并后的有序数组数据 10 int[] tmp = new int[endSecond - beginFrist + 1]; 11 //初始化各中间变量 12 int tempFristLocation = beginFrist, tempSecondLocation= beginSecond, tempLocation = 0; 13 //遍历两个数组,并排序,知道一个数组被全部遍历为止。 14 while (tempFristLocation < beginSecond && tempSecondLocation <= endSecond) { 15 if (a[tempFristLocation] <= a[tempSecondLocation]) { 16 tmp[tempLocation] = a[tempFristLocation]; 17 tempFristLocation++; 18 } else { 19 tmp[tempLocation] = a[tempSecondLocation]; 20 tempSecondLocation++; 21 } 22 tempLocation++; 23 } 24 //把另一个为被完全遍历的数组剩余部分加在temp最后,因为两个数组本身有序,所以剩余部分一定比现有部分大。 25 while (tempFristLocation < beginSecond) { 26 tmp[tempLocation] = a[tempFristLocation]; 27 tempFristLocation++; 28 tempLocation++; 29 } 30 while (tempSecondLocation <= endSecond) { 31 tmp[tempLocation] = a[tempSecondLocation]; 32 tempSecondLocation++; 33 tempLocation++; 34 } 35 //复制temp到给的数组a 36 System.arraycopy(tmp, 0, a, beginFrist, tmp.length); 37 }
归并主算法如下:
1 /** 2 * 归并主算法,此处使用了尾递归,理论上可转换为迭代 3 * @param a 需排序数组 4 * @param len 每个堆内的元素个数 5 */ 6 public static void mergeSort(int[] a, int len) { 7 //初始化数据 8 int size = a.length,s; 9 //取到中间位置,此处<<的位运算可理解为*2 10 int mid = size / (len << 1); 11 // 归并到只剩一个有序集合的时候结束算法 12 if (mid == 0) 13 return; 14 //进行一趟归并排序 15 for (int i = 0; i < mid; ++i) { 16 //s为第一个有序数组的开始位置 17 s = i * 2 * len; 18 //调用归并排序 19 merge(a, s, s + len, (len << 1) + s - 1); 20 } 21 /* 此处由于len一定为2的N次幂,所以对size与((len << 1) - 1)进行与运算,只要不为0即可代表size不是len的倍数,即len不能被zise整除 22 也就是说, mid次归并后还有部分数据没有得到归并处理*/ 23 int c = size & ((len << 1) - 1); 24 25 //将剩下的数和最后一个有序集合归并 26 if (c != 0) 27 merge(a, size - c - 2 * len, size - c, size - 1); 28 // -------递归执行下一趟归并排序------// 29 mergeSort(a, 2 * len); 30 }
测试方法如下:
1 public static void main(String[] args) { 2 Random random = new Random(); 3 int[] a = new int[8]; 4 for (int i = 0; i < a.length; i++) { 5 a[i]=random.nextInt(100); 6 } 7 mergeSort(a, 1); 8 for (int i = 0; i < a.length; ++i) { 9 System.out.print(a[i] + " "); 10 } 11 }