《数据结构与算法》
本文来源于liuyubobobo的“算法与数据结构--综合提升篇”视频教程
选择排序、插入排序都是O(n^2)复杂度的排序算法(即这两种算法都使用了两重for循环)
归并排序是O(n log n)时间复杂度的排序算法,适合大数据量排序,适合内存充足的场景使用。
归并排序思路
上面归并思路中最理解、最难处理的点在于:相邻数组段合并时,怎样才能实现元素排序。
这就需要创建一个临时数组储存数组段中的元素(使用空间换时间的思想在算法中很常见)
以下是实现代码
// 归并排序
public void mergeSort(int[] arr){
mergeSegment(arr, 0, arr.length-1);
}
/**
* 归并排序私有方法
* @param arr
* @param minIndex 最小下标
* @param maxIndex 最大下标
*/
private void mergeSegment(int[] arr, int minIndex, int maxIndex){
if (minIndex >= maxIndex){
return;
}
int middleIndex = (minIndex+maxIndex)/2;
// 数组平分为两段,然后再递归对分段后的数组再分段
mergeSegment(arr, minIndex, middleIndex);
mergeSegment(arr, middleIndex+1, maxIndex);
// 两段数组合并、排序
segmentsMergeSort(arr, minIndex, middleIndex, maxIndex);
}
/**
* 合并、排序相邻的两个数组段
* @param arr
* @param minIndex 左边数组段最小下标
* @param middleIndex 中间下标
* @param maxIndex 右边数组段最大下标
*/
private void segmentsMergeSort(int[] arr, int minIndex, int middleIndex, int maxIndex){
// 临时数组,存储两个数组段的元素
int[] segmentArr = new int[maxIndex - minIndex + 1];
//将数组arr[minIndex]到arr[maxIndex]的元素拷贝到临时数组中
for (int i = minIndex; i <= maxIndex; i++){
segmentArr[i-minIndex] = arr[i];
}
// 定义分段数组中的下标
int leftSegmentIndex = minIndex, rightSegmentIndex = middleIndex+1;
// 比较两数组段元素大小,将临时数组segmentArr中的元素添加到原数组中
for (int currentIndex=minIndex; currentIndex<=maxIndex; currentIndex++){
if (leftSegmentIndex > middleIndex){
// 左边的数组段遍历完了,右边数组段元素加到原数组中
arr[currentIndex] = segmentArr[rightSegmentIndex - minIndex];
rightSegmentIndex++;
}else if (rightSegmentIndex > maxIndex){
// 右边的数组段遍历完了,左边数组段元素加到原数组中
arr[currentIndex] = segmentArr[leftSegmentIndex - minIndex];
leftSegmentIndex++;
}else if (segmentArr[leftSegmentIndex - minIndex] < segmentArr[rightSegmentIndex - minIndex]){
// 左边数组段元素小
arr[currentIndex] = segmentArr[leftSegmentIndex - minIndex];
leftSegmentIndex++;
}else {
// 右边数组段元素小
arr[currentIndex] = segmentArr[rightSegmentIndex - minIndex];
rightSegmentIndex++;
}
}
}
/**
* 生成随机数组
* @param n 数组长度
* @param randomL 数组最小元素
* @param randomR 数组最大元素
*/
public static int[] generateIntArray(int n, int randomL, int randomR){
int[] arr = new int[n];
for (int i=0; i<arr.length; i++){
int num = new Random().nextInt(randomR - randomL + 1) + randomL;
arr[i] = num;
}
return arr;
}
@Test
public void test_mergeSort() {
int[] arr1 = generateIntArray(100000, 0, 100000);
mergeSort(arr1);
for (int i=0; i<arr1.length; i++){
System.out.println(arr1[i]);
}
}
归并排序的mergeSegment方法还可稍加优化
private void mergeSegment(int[] arr, int minIndex, int maxIndex){
//if (minIndex >= maxIndex){
// return;
//}
// 优化1
// 当需要排序的数据量很小的时候,使用插入排序提高效率
// 这种优化适用于很多的高级算法
// 插入排序请看这篇博客 https://blog.csdn.net/u010606397/article/details/102546480
if (maxIndex - minIndex <= 15){
for (int i = minIndex; i<=maxIndex; i++){
int e = arr[i];
int j;
for (j=i; j-1>=minIndex && arr[j-1] > e; j--){
arr[j] = arr[j-1];
}
arr[j] = e;
}
return;
}
int middleIndex = (minIndex+maxIndex)/2;
// 数组平分为两段,然后再递归对分段后的数组再分段
mergeSegment(arr, minIndex, middleIndex);
mergeSegment(arr, middleIndex+1, maxIndex);
// 优化2
// 只在左数组段最后一个元素比右数组段第一个元素大的时候才执行合并
if (arr[middleIndex] > arr[middleIndex+1]){
// 两段数组合并、排序
segmentsMergeSort(arr, minIndex, middleIndex, maxIndex);
}
}