归并排序
1、原理
归并排序是一种概念上最简单的排序算法,与快速排序一样,归并排序也是基于分治法的。 **归并排序将待排序的元素序列分成两个长度相等的子序列,为每一个子序列排序,然后再将他们合并成一个子序列。**合并两个子序列的过程也就是两路归并。
2、复杂度
归并排序是一种稳定的排序算法,归并排序的主要问题在于它需要一个与待排序数组一样大的辅助数组空间。由于归并排序每次划分时两个子序列的长度基本一样,所以归并排序最好、最差和平均时间复杂度都是nlog2n。
我们可以通过下图非常容易看懂归并排序的过程:
要将两个排好序的子序列合并为一个子序列的方法:每次都是从未比较的两个子序列的最小值中选出一个更小值。
3、java代码
- 形参有辅助数组时
/**
* 递归进行归并排序
*
* @param nums 待排序数组序列
* @param start 排序序列起始位
* @param end 排序序列终止位
* @param temp 辅助数组 存在中间排序结果,长度需于原数组相同
*/
public void mergeSort(int nums[], int start, int end, int temp[]) {
if (start >= end) return;//当前序列只有一个元素时结束递归
int middle = (start + end) / 2;//划分子序列的边界
mergeSort(nums, start, middle, temp);//对左侧子序列进行递归归并排序
mergeSort(nums, middle + 1, end, temp);//对右侧子序列进行递归归并排序
merge(nums, start, middle, end, temp);//合并两个子序列 这两个子序列一定为有序(序列中只有一个元素时当成有序)
}
/**
* 两路归并算法 ,将两个排好序的子序列合并为一个子序列
*
* @param nums 待排序数组序列
* @param start 左侧序列的起点
* @param middle 左侧序列的终点
* @param end 右侧序列的终点 右侧序列的起点默认为 middle+1
* @param temp 辅助数组 存在中间排序结果,长度需于原数组相同
*/
public void merge(int[] nums, int start, int middle, int end, int temp[]) {
//辅助数组的存放指针
int flag = start;
//左侧序列的检测指针
int start1 = start;
//右侧序列的检测指针
int start2 = middle + 1;
//将两个序列依据最小值不断合并直至一个序列中的合并完
while (start1 <= middle && start2 <= end) {
if (nums[start1] < nums[start2]) {
temp[flag] = nums[start1];
start1++;
} else {
temp[flag] = nums[start2];
start2++;
}
flag++;
}
//如果左侧序列未合并完,将剩余的数直接加到合并序列尾部
while (start1 <= middle) {
temp[flag] = nums[start1];
start1++;
flag++;
}
//同上
while (start2 <= end) {
temp[flag] = nums[start2];
start2++;
flag++;
}
//复制合并完的序列到原数组
for (int i = start; i <= end; i++) {
nums[i] = temp[i];
}
}
- 形参无辅助数组时,合并时构建相应大小的辅助数组即可。
public void mergeSort(int nums[], int start, int end) {
if (start >= end) return;//当前序列只有一个元素时结束递归
int middle = (start + end) / 2;//划分子序列的边界
mergeSort(nums, start, middle);//对左侧子序列进行递归归并排序
mergeSort(nums, middle + 1, end);//对右侧子序列进行递归归并排序
merge(nums, start, middle, end);//合并两个子序列
}
public void merge(int[] nums, int start, int middle, int end) {
int[] temp = new int[end - start + 1];
int flag = 0;
int start1 = start;
int start2 = middle + 1;
while (start1 <= middle && start2 <= end) {
if (nums[start1] < nums[start2]) {
temp[flag] = nums[start1];
start1++;
} else {
temp[flag] = nums[start2];
start2++;
}
flag++;
}
while (start1 <= middle) {
temp[flag] = nums[start1];
start1++;
flag++;
}
while (start2 <= end) {
temp[flag] = nums[start2];
start2++;
flag++;
}
for(int i = 0;i<=temp.length;i++){
nums[i+start]=temp[i];
}
}