归并排序(Merge sort)是利用"归并"技术来进行排序的。归并是指将若干个已排序的子文件合并成一个有序的文件。
1、归并排序的基本思想
两个归并算法的基本思想:设有两个有序的子文件(相当于输入堆)放在同一向量中相邻的位置上:A[low...m],A[m+1...high],先将他们合并到一个暂存的向量Temp(相当于 输出堆)中,待合并完成后将Temp复制回A[low...high]中。
归并排序有两种实现方法:自底向上和自顶向下。
自底向上方法的基本思想:
(1) 第1趟归并排序时,将待排序的文件A[1...n]看作n个长度为1的有序子文件,将这些子文件两两归并。若n为偶数,则得到n/2个长度为2的有序子文件;若n为奇数,则 最后一个子文件不参与归并。所以本趟归并完成后,前n/2个有序子文件长度为2,但最后一个子文件长度为1。
(2) 第2趟归并则是将第1趟归并所得到的n/2个有序的子文件两两归并,如此反复,直到最后得到一个长度为n的有序文件为止。
(3) 上述每次归并操作,均是将两个有序的子文件合并成一个有序的子文件,所以称为"二路归并排序"。类似地,有k(k>2)路归并排序。
自顶向下方法的基本思想更为简洁:设归并排序的当前区间是A[low...high],步骤如下:
(1) 分解:将当前的区间一分为而,即求分裂点。
(2) 求解:递归地对两个子区间A[low...mid]和A[mid+1...high]进行归并排序。
(3) 组合:将已排序的两个子区间A[low...mid]和A[mid+1...high]归并为一个有序的区间R[low...high]。
(4) 递归的终止条件:子区间长度为1(一个记录自然有序)。
2、思想
多次将两个或两个以上的有序表合并成一个新的有序表。
3、算法复杂度
最好的情况下:一趟归并需要n次,总共需要logN次,因此为O(N*logN)
最坏的情况下,接近于平均情况下,为O(N*logN)
说明:对长度为n的文件,需进行logN 趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlgn)。
4、稳定性
归并排序最大的特色就是它是一种稳定的排序算法。归并过程中是不会改变元素的相对位置的。
缺点是,它需要O(n)的额外空间。但是很适合于多链表排序。(例如将两个有序链表合并成一个有序的链表)
5、代码实现
//实现归并排序的算法(使用自顶向下的方法)
//归并排序是复杂的排序。
//1.在归并排序之前,先分配一个数组长度大小的临时空间,用于存放数组归并排序的结果.
//2.先将整个数组每次二分,二分的终止条件是低位下标大于等于高位,递归调用直到终止.
//3.调用归并时,传入左侧开始位置、右侧开始位置和右侧结束位置,临时数组开始位置为左侧位置,左侧结束位置为右侧开始位置-1
//4.确定临时数组长度为右末-左初+1.
//5.先从左初位置和右初位置开始遍历,比较两端的元素值,把较小的值放入temp临时数组中,再从中找出次小的值存入数组中,直到左初位置大于左末,或者是右初位置大于右末位置。
//6.不管怎么操作肯定会有一段含有剩余元素,遍历左初到左末,右初到右末位置,把剩余元素放入数组中
//7.把临时数组中的数据拷贝到原始数组中,循环次数为数组长度,从右末位置开始。
//8.循环2-8步,直到原始数组有序为止。
#include<iostream>
using namespace std;
//将分值的两端按大小次序填入临时数组,最后临时数组拷贝到原始数组中
//lPos到rPos-1为一端,rPos到rEnd为另外一端
static void Merge(int a[], int temp[], int lPos, int rPos, int rEnd)
{
int i, NumElement, lEnd, tempPos;
tempPos = lPos;
lEnd = rPos - 1; //从左端开始
NumElement = rEnd - lPos + 1; //数组长度
while ((lPos <= lEnd) && (rPos <= rEnd))
{
if (a[lPos] <= a[rPos]) //比较两端的元素值
temp[tempPos++] = a[lPos++]; //把较小的值先放入temp临时数组中
else
temp[tempPos++] = a[rPos++];
}
//到这里,左端大或者右端只能有一端还可能含有剩余元素
while (lPos <= lEnd) //把左端剩余的元素放入temp中
temp[tempPos++] = a[lPos++];
while (rPos <= rEnd) //把右端剩余的元素放入temp中
temp[tempPos++] = a[rPos++];
for (i = 0; i < NumElement; i++, rEnd--)
a[rEnd] = temp[rEnd]; //把临时数组拷贝到原始数组中
}
static void msort(int a[], int temp[], int low, int high)
{
if (low >= high) //结束条件,当low和high中间没有元素return
return;
int middle = (low + high) / 2; //计算分裂点
msort(a, temp, low, middle); //对子区间[low,middle]递归做归并排序
msort(a, temp, middle + 1, high); //对子区间[middle+1,high]递归做归并排序
Merge(a, temp, low, middle + 1, high); <span style="white-space:pre"> </span>//组合,把两个有序区合并为一个有序区
}
static void merge_sort(int a[], int len)
{
int* temp = NULL; //分配临时数组空间
temp = new int[len];
if (temp != NULL)
{
msort(a, temp, 0, len - 1); //调用msort归并排序
delete[] temp; //释放临时数组内存
}
}
static void print_array(int a[], int length)
{
for (int i = 0; i < length; i++) //打印元素
{
cout << a[i] << " ";
}
}
void main9mianshiti7()
{
int a[] = { 8, 6, 1, 3, 5, 2, 7, 4 };
cout << "before merge sort:";
print_array(a, 8);
merge_sort(a, 8); //归并排序
cout << "\nafter merge sort:";
print_array(a, 8);
system("pause");
}
6、测试结果
before merge sort::8 6 1 3 5 2 7 4
after merge sort:1 2 3 4 5 6 7 8