对于归并排序的理解有个很好的例子就是高考排名,我们知道高考的录取是根据全省的学生排名来进行的,那这个学生的排名是怎么得出来的呢?
我们现在假设改卷的最小单位是学校(假设!!!实际上是全省统一改卷,我们对于高考还是很严格),那你在一个学校就能够将你的学生的名次排出来,但这个排名还不够,我们需 要将整个县的中学成绩再进行一次汇总,这样原来排好序的学校排名就合并成了全县学生的 排名,然后我们再将一个市的各个县学生成绩排名合并就得到了我们的全市的排,再将各个市的成绩排名进行汇总,这样就得到了我们的整个省的排名了。
在进行排名的过程中我们的最小单位是学校,然后再逐级合并就得到了我们的最终稿全省的排名,这个过程就是我们的归并排序,就已有序的序列(我们最小的序列——一个元素的序列)不断地合并,最终得到整个序列的排序结果。具体的算法过程如下图所示:
整个算法的java实现如下:
package sort;
/**
* 实现希尔排序,写入排序的算法时间复杂度是O(NlogN),空间复杂度是O(N),归并排序是一个稳定的排序算法,
* 也就是说归并排序不不会将原来有序的数组元素下标改变。
*/
import java.util.Arrays;
public class ShellSort{
public static void main(String[] args) {
int[] arr = {50,10,90,30,70,40,80,60,20};
mergeSort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 希尔排序算法,将输入的数组进行希尔排序
* @param arr 待排序数组
*/
public static void mergeSort(int[] arr) {
sort(arr, 0, arr.length - 1);
}
/**
* 实现希尔排序中数组最小化切割及合并
* @param arr 待排序的数组
* @param L 数组的左边界
* @param R 数组的有边界
*/
public static void sort(int[] arr, int L, int R) {
if(L == R) {
return;
}
int mid = L + ((R - L) >> 1);
sort(arr, L, mid);
sort(arr, mid + 1, R);
merge(arr, L, mid, R);
}
/**
* 两个数组的合并,合并时完成新数组的排序过程,可以说是归并排序的核心部分
* @param arr 待合并的数组
* @param L 数组的左边界
* @param mid 待排序数组的中间界限,在mid的左右两边都素hi已完成排序的数组
* @param R 数组的右边界
*/
public static void merge(int[] arr, int L, int mid, int R) {
int[] temp = new int[R - L + 1];
int i = 0;
int p1 = L;
int p2 = mid + 1;
while(p1 <= mid && p2 <= R) {
temp[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while(p1 <= mid) {
temp[i++] = arr[p1++];
}
while(p2 <= R) {
temp[i++] = arr[p2++];
}
for(i = 0; i < temp.length; i++) {
arr[L + i] = temp[i];
}
}
}
附上一张程杰老师(《大话数据结构》作者)讲解归并时的一张图,可以说把归并排序的分割与合并描述的非常清楚了!