归并排序
归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
算法描述
- 分解: 将原序列分解成length长度的若干子序列
- 求解子问题: 将相邻的两个子序列调用Merge算法合并成一个有序子序列
- 合并: 由于整个序列存放在数字a中,排序过程是就地进行的,合并步骤不需要执行任何操作.
例如:
示例程序
// merge_sort.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<malloc.h>
using namespace std;
void disp(int a[], int n)//输出a中所有元素
{
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
}
void merge(int a[], int low, int mid, int high)
//将a[low..mid]和a[mid+1..high]两个相邻的有序子序列归并成一个有序子序列a[low..high]
{
int i = low, j = mid + 1, k = 0;
int* tampa;
tampa = (int*)malloc((high - low + 1) * sizeof(int));//临时存放数组
while (i<=mid&&j<=high)//扫描1表和2表
{
if (a[i] <= a[j])
{
tampa[k] = a[i];
i++; k++;
}
else
{
tampa[k] = a[j];
j++; k++;
}
}
while (i <= mid)//将1表剩下的放进去
{
tampa[k] = a[i];
i++; k++;
}
while (j <= high)//将2表剩下的放进去
{
tampa[k] = a[j];
j++; k++;
}
for (k = 0, i = low; i <= high; i++, k++)//将tampa复制回a中
{
a[i] = tampa[k];
}
free(tampa);
}
void mergePass(int a[], int length, int n)//一趟二路归并排序
{
int i;
for (i = 0; i + 2 * length - 1 < n; i = i + 2 * length)
{
merge(a, i, i + length - 1, i + 2 * length - 1);
}
if (i + length - 1 < n)//若余下两个子表,后者长度小于length
{
merge(a, i, i + length - 1, n - 1);//归并这两个表
}
}
void merge_sort(int a[], int n)//二路归并算法
{
int length;
for (length = 1; length < n; length = 2 * length)
{
mergePass(a, length, n);
}
}
int main()
{
int n = 10;
int a[] = { 2,5,1,7,10,6,9,4,3,8 };
cout << "排序前:" << endl;
disp(a, n);
cout << endl;
merge_sort(a, n);
cout << "排序后:" << endl;
disp(a, n);
return 0;
}
比较
归并排序是稳定的排序.即相等的元素的顺序不会改变.如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时输出的 1(1) 2(3) 2(4) 3(2) 5(5) 中的2 和 2 是按输入的顺序.这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要。归并排序的比较次数小于快速排序的比较次数,移动次数一般多于快速排序的移动次数。
用途
速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列,应用见2011年普及复赛第3题“瑞士轮”的标程。