归并排序算法思想:
归并排序(Merge Sort)就是利用归并的思想实现排序的方法。它的原理是假设初始学列有N个记录,则可以看成是N个有序的子序列,每个子序列的长度为1,然后两两归并,得到 N/2 个长度为2或1的有序子序列,再两两归并…如此重复,直至得到一个长度为N的有序序列为止,这种排序方法称为2路归并排序。
分而治之:
归并排序,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之
图示:
如图所示:第一步分
- 分解:将当前的无序序列一分为二,即求其中间点centerIndex = (startIndex + endIndex) / 2。
- 求解:递归地对分裂的两部分进行分裂…直到序列长度为1时停止。
- 合并:将分开的两个有序的数组合并为一个数组。
代码实现:
private static void chaiFen(int[] arr, int startIndex, int endIndex) {
//计算中间索引
int centerIndex = (startIndex + endIndex) / 2;
if (startIndex < endIndex) { //递归拆分
//拆分左边
chaiFen(arr, startIndex, centerIndex);
//拆分右边
chaiFen(arr, centerIndex + 1, endIndex);
//归并
mergeMethod(arr, startIndex, centerIndex, endIndex);
}
}
合并有序的子序列:再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。
代码实现
public class 归并排序 {
public static void main(String[] args) {
//int[] arr = {1, 3, 6, 9, 2, 4, 7, 8};
//
//mergeMethod(arr, 0, 3, arr.length - 1);
//
//System.out.println(Arrays.toString(arr));
//上面归并测试好,下来我们给一个无序的数组,进行拆分,然后归并
int[] arr = {1, 3, 6, 9, 2, 4, 7, 8, 10, 0, 20, 100, 89, 30, 26};
chaiFen(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
private static void chaiFen(int[] arr, int startIndex, int endIndex) {
//计算中间索引
int centerIndex = (startIndex + endIndex) / 2;
if (startIndex < endIndex) { //递归拆分
//拆分左边
chaiFen(arr, startIndex, centerIndex);
//拆分右边
chaiFen(arr, centerIndex + 1, endIndex);
//归并
mergeMethod(arr, startIndex, centerIndex, endIndex);
}
}
/**
* @param arr 要归并的数组
* @param startIndex 开始索引
* @param centerIndex 中间元素所对应的索引 不是数组长度除以2 是 (arr.length - 1)/2
* @param endIndex 结束索引
*/
private static void mergeMethod(int[] arr, int startIndex, int centerIndex, int endIndex) {
//定义临时数组
int[] tempArr = new int[endIndex - startIndex + 1];
//定义第一个数组开始的索引
int i = startIndex;
//定义第二个数组开始的索引
int j = centerIndex + 1;
//定义临时数组的开始索引
int index = 0;
//遍历数组进行对比
while (i <= centerIndex && j <= endIndex) {
//如果第一个序列的第一个元素,小于等于第二个序列的第一个元素
if (arr[i] <= arr[j]) {
tempArr[index] = arr[i]; //把小的元素,放到临时数组中
i++; //递增一下索引,下次对比第二个元素
} else {
tempArr[index] = arr[j];
j++;
}
index++; //记得让临时数组的的索引递增
}
//因为两个序列的的元素个数,不是均等的,通过上面循环比较完后会有剩余元素,可能左边序列会有剩余,也可能右边元素会有剩余
while (i <= centerIndex) {
tempArr[index] = arr[i]; //把小的元素,放到临时数组中
i++; //递增一下索引,下次对比第二个元素
index++;//临时数组的的索引递增
}
while (j <= endIndex) {
tempArr[index] = arr[j];
j++;
index++;
}
//这个时候,排序好的元素就会放到临时数组中
// System.out.println(Arrays.toString(tempArr));
//然后我们遍历临时数组,将临时数组中的元素,放置到原数组中
for (int k = 0; k < tempArr.length; k++) {
//注意这里k要加上起始索引
arr[k + startIndex] = tempArr[k];
}
}
}