归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)[1]。
归并算法分为两步:
- 分:将一个待排序的数组分为两个待排序的数组,并分别对其进行排序。
- 治:将两个有序数组合并成一个有序数组。
以下是java实现
public void mergeSort(int[] arr, int low, int high) {
//待排序原数组arr,待排序的区域为arr[low:high]
if (low >= high) //low不小于high时待排序区域只有一个元素,退出当前层
return;
int mid = (low + high) / 2; //将待排序区域对半分为两个子区域,递归调用
mergeSort(arr, low, mid);
mergeSort(arr, mid + 1, high);
merge(arr, low, mid + 1, high); //merge()方法将两个有序区域合并成一个有序区域
}
public void merge(int[] arr, int low, int mid_plus1, int high) { //两个待排序区域分别为arr[low:mid],arr[mid+1,high]
int[] assist = new int[high - low + 1]; //assist[]数组用于保存合并后的有序区域,并最后复制到原数组arr对应区域
int i = 0;
int mid = mid_plus1 - 1;
int start = low; //start用于保存待合并的初始位置
while (low <= mid && mid_plus1 <= high) { //两个待排序区域分别从low,mid+1位置开始比较,较小的复制到assist中
//当arr[low:mid]复制完毕后 low > mid, 当arr[mid+1:high]复制完毕后 mid+1 > high,满足其中一个条件时退出while循环
if (arr[low] <= arr[mid_plus1])
assist[i++] = arr[low++];
else
assist[i++] = arr[mid_plus1++];
}
while (low <= mid) {
assist[i++] = arr[low++];
}
while (mid_plus1 <= high) {
assist[i++] = arr[mid_plus1++];
}
for (int j = 0; j < assist.length; j++)
arr[start++] = assist[j];
}
最后测试代码如下
package com.sort;
public class Test_MergeSort {
public void test() {
int[] arr = {1, 11, 16, 4, 6, 3, 9, 2, 11};
MergeSort meSort = new MergeSort();
meSort.mergeSort(arr,0,arr.length-1);
for (int ele : arr) {
System.out.println(ele);
}
}
}
public class Main {
public static void main(String[] args) {
// write your code here
Test_MergeSort test = new Test_MergeSort();
test.test();
}
}
时间效率分析:
总时间=分解时间+解决问题时间+合并时间。分解时间就是把一个待排序序列分解成两序列,时间为一常数,时间复杂度o(1).解决问题时间是两个递归式,把一个规模为n的问题分成两个规模分别为n/2的子问题,时间为2T(n/2).合并时间复杂度为o(n)。总时间T(n)=2T(n/2)+o(n).这个递归式可以用递归树来解,其解是o(nlogn).此外在最坏、最佳、平均情况下归并排序时间复杂度均为o(nlogn).
空间效率分析:
空间消耗主要在于每一次合并两个有序数组时调用merge()方法,借助了辅助数组assist来存储合并结果,因为每一次调用完该数组都被回收,所以空间消耗最大的一次是最后一次调用将两个有序区域合并成原数组,此时assist长度为n,故空间代价为O(n)。
稳定性分析:
如果原来a在b前面,如果a=b,排序后a仍然在b前面,则排序稳定。显然归并排序的排序过程是稳定的