【算法】排序(四)归并排序
正文之前
归并排序(Merge sort),是建立在归并操作上的一种有效的排序算法,效率为O(n logn),1945年由约翰冯诺依曼首次提出,该算法是采用分治法的一个非常典型的应用,且各层分治递归可以同时进行。
——Wikipedia
本文将介绍以下内容
排序原理
算法实现(JAVA)
测试阶段
算法分析
正文
排序原理
采用分治的做法,将一个数组分为两个序列,将序列继续拆分达到最小,分别排序,最后将有序的序列归并,达到全部有序的效果。
算法实现
1. 归并操作
public class MergeSort {
private static int[] temp; //临时数组
public static void merge(int[] a, int low, int mid, int high){
int i = low; //第一序列首
int j = mid + 1; //第二序列首
for (int k = low; k <= high ; k++) {
temp[k] = a[k]; //复制元素
}
for (int k = low; k <= high; k++) {
if(i > mid){ //第一序列的全部元素已归并
a[k] = temp[j++];
}
else if(j > high){ //第二序列的全部元素已归并
a[k] = temp[i++];
}
else if(temp[j] < temp[i]){ //第二序列的元素小于第一序列元素
a[k] = temp[j++];
}
else{
a[k] = temp[i++]; //第一序列的元素小于第二序列
}
}
}
}
归并的操作就是依次从序列中选取元素,两个序列中的元素比较,哪个小选哪个,一个序列全部选完了,就直接选择另一个序列的全部元素
2. 排序操作
public static void sort(int[] a){
temp = new int[a.length];
sort(a, 0, a.length - 1); //排序整个数组
}
private static void sort(int[] a, int low, int high){
if(low >= high){
return;
}
int mid = low + (high - low) / 2;
sort(a, low, mid); //排序左半边
sort(a, mid + 1, high); //排序右半边
merge(a, low, mid, high); //归并
}
sort 方法的作用就是递归执行自己,直到将序列拆分至最小,然后用 merge 方法来排序,最后归并
整个排序过程的代码块就是如此:
public class MergeSort {
private static int[] temp; //临时数组
private static void merge(int[] a, int low, int mid, int high){
int i = low; //第一序列首
int j = mid + 1; //第二序列首
for (int k = low; k <= high ; k++) {
temp[k] = a[k]; //复制元素
}
for (int k = low; k <= high; k++) {
if(i > mid){ //第一序列的全部元素已归并
a[k] = temp[j++];
}
else if(j > high){ //第二序列的全部元素已归并
a[k] = temp[i++];
}
else if(temp[j] < temp[i]){ //第二序列的元素小于第一序列元素
a[k] = temp[j++];
}
else{
a[k] = temp[i++]; //第一序列的元素小于第二序列
}
}
}
private static void sort(int[] a){
temp = new int[a.length];
sort(a, 0, a.length - 1); //排序整个数组
}
private static void sort(int[] a, int low, int high){
if(low >= high){
return;
}
int mid = low + (high - low) / 2;
sort(a, low, mid); //排序左半边
sort(a, mid + 1, high); //排序右半边
merge(a, low, mid, high); //归并
}
}
测试阶段
public static void main(String[] args){
int[] a = new int[10];
for (int i = 0; i < 10; i++) {
a[i] = (int)(Math.random() * 100);
System.out.print(a[i] + " ");
}
System.out.println();
sort(a);
for (int i = 0; i < 10; i++) {
System.out.print(a[i] + " ");
}
}
生成十个随机数字,并调用排序方法,接下来选取几组测试结果:
算法分析
1. 特点:
归并排序将所有元素复制到一个辅助数组中,将归并后的结果放入原数组中,不需要额外的空间
2. 时间复杂度
这里需要用一些数学公式:
-
设数组的长度为N,用 T(N) 表示排序一个长度为N的数组所需要的比较次数,可想而知,T(0) 和 T(1) 为0
-
将数组的两边分别排序各需要 N/2 次比较(上限),归并所需次数为N(上限)
-
所以 T(N) ≤ T(N/2) + T(N/2) + N
-
设 N = 2n且不等式的等号成立:
T(2n) = 2T(2n-1) + 2n
(N / 2 = 2n - 1) -
等式两边同时除以2n,得到:
T(2n) / 2n = T(2n-1) / 2n - 1 + 1 -
由上式可以得到:
T(2n - 1) / 2n - 1 = T(2n-2) / 2n - 2 + 1 -
将 6 的结果代入 5 中,得到:
T(2n) / 2n = T(2n-2) / 2n - 2 + 1 + 1 -
重复第7步,直到得到如下结果:
T(2n) / 2n = T(20)/20 + n
(共计 n - 1 步) -
等式两边同时乘以 2n ,得到结果:
T(N) = 2n T(20) + n 2n = NlogN
( n = logN)
所以,算法复杂度为O(nlogn)
3. 稳定性
当两个元素相等时,归并排序不会将两者交换,所以归并排序是稳定的
关于归并排序的介绍就到此了,谢谢!