归并排序(Merge Sort)
1、概述
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。
2、算法原理
归并操作的工作原理如下:
1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
4. 重复步骤3直到某一指针超出序列尾将另一序列剩下的所有元素直接复制到合并序列尾
又如:
将序列每相邻两个数字进行归并操作(merge),形成floor(n/2)个序列,排序后每个序列包含两个元素。将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素。重复步骤2,直到所有元素排序完毕。
3、算法分析
①时间复杂度
时间复杂度为O(nlogn)这是该算法中最好、最坏和平均的时间性能。
空间复杂度为 O(n)。
比较操作的次数介于(nlogn) / 2和nlogn - n + 1。
赋值操作的次数是(2nlogn)。归并算法的空间复杂度为:0(n)。
归并排序比较占用内存,但却是一种效率高且稳定的算法。
②算法稳定性
归并排序是稳定的排序。
即相等的元素的顺序不会改变。
如输入记录:
1(1)、3(2)、2(3)、2(4)、5(5) (括号中是记录的关键字)时输出的
1(1)、2(3)、2(4)、3(2)、5(5)中的2和2是按输入的顺序。
这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要,这也是它比快速排序优势的地方。
4、算法实例
代码如下:
/**
* @author Hanlin Wang
*/
public class MergeSort {
public static void main(String[] args) {
//创建数据
int[] data = {1,2,4,3};
//进行归并排序
sort(data);
//排序后,打印结果
String txt = "";
for (int i : data) {
txt += i + ",";
}
String res = txt.substring(0, txt.length()-1);
System.out.println(res);
}
//归并排序的入口
public static void sort(int[] data){
mergeSort(data, 0, data.length - 1);
}
//归并排序
public static void mergeSort(int[] data, int low, int high){
//若low<high,说明可以再分
if (low<high) {
int center = (low+high)/2;
mergeSort(data, low, center);
mergeSort(data, center+1, high);
merge(data, low, center, high);
}
}
//归并这一步骤
public static void merge(int[] data, int low, int center, int high){
//创建临时数组以后返还给data
int[] tmpArr = new int[data.length];
//临时数组的角标
int index = low;
//第二部分的起始位
int mid = center+1;
//供以后将临时数组的值赋给data数组做初始化的临时角标
int tmp = low;
//当切仅当low小于center,mid小于high进入循环
while (low<=center&&mid<=high) {
//如果当前角标的low的值小于mid值,将low角标值赋值给tmpArr,并同时将角标+1
if (data[low]<=data[mid]) {
tmpArr[index++] = data[low++];
} else {
tmpArr[index++] = data[mid++];
}
}
//处理剩余的值。事实上,只会进入其中的一个循环
while (low<=center) {
tmpArr[index++] = data[low++];
}
while (mid<=high) {
tmpArr[index++] = data[mid++];
}
//将tmpArr的值塞入data中,完成合并
while (tmp<=high) {
data[tmp] = tmpArr[tmp++];
}
}
}
运行结果:
1,2,3,4