本文主要代码及思路来源于:【算法设计与分析(第五版)】【王晓东】
将待排序元素分成大小大致相同的2个子集合,
分别对2个子集合进行排序,
最终将排好序的子集合合并成为所要求的排序
//递归描述
void MergeSort(Type a[ ], int left, int right)
{
if (left
对于算法MergeSort,还可以从多方面对它进行改进。例如,从分治策略的机制入手,容易消除算法中的递归。
事实上,算法MergeSort的递归过程只是将待排序集合一分为二,直至待排序集合只剩下一个元素为止,然后不断合并两个排好序的数组段。
按此机制,可以首先将数组a中相邻元素两两配对。用合并算法将它们排序,构成n/2组长度为2的排好序的子数组段,然后再将它们排序成长度为4的排好序的子数组段,如此继续下去,直至整个数组排好序。
//消去递归后的合并排序算法
void MergeSort(int *a,int n)
{
int b[n];
int s=1;//子数组长度
while(s
#include
#include
using namespace std;
template
void Merge(Type c[], Type d[], int l, int m, int r)
// 合并c[l:m]和c[m+1:r]到d[l:r]
{
int i=l; // 左子数组的当前处理索引
int j=m+1; // 右子数组的当前处理索引
int k=l; // 合并结果索引
while (i<=m && j<=r)
{
if (c[i]<=c[j]) d[k++]=c[i++];
else d[k++]=c[j++];
}
if (i>m) for(int q=j; q<=r; q++) d[k++]=c[q];
else for(int q=i; q<=m; q++) d[k++]=c[q];
}
template
void MergePass(Type x[], Type y[], int s, int n)
// 将x中长度为s的相邻子数组合并,结果存入y中
// n: 数组元素数量
{
int i=0; // 子数组起始位置
// 合并度为s的相邻子数组
while (i+s+s<=n)
{
Merge(x, y, i, i+s-1, i+s+s-1);
i+=s+s;
}
// 剩下元素数量少于2s
if (i+s
void MergeSort(Type a[], int n)
{
Type *b=new Type [n];
int s=1; // 子数组长度
while (s
void OutputArray(Type elements[], int num_elements)
{
cout<
#include
using namespace std;
void Merge(int *c,int *d,int l,int m,int r)
// 合并c[l:m]和c[m+1:r]到d[l:r]
{
int i=l,j=m+1,k=l;//左右子数组的当前处理索引,合并后的索引
while(i<=m && j<=r) {
if(c[i]<=c[j]) d[k++]=c[i++];
else d[k++]=c[j++];
}
if(i>m)//左子数组全部并入数组d,同时右子数组还有值未并入数组d
for(int q=j; q<=r; q++)
d[k++]=c[q];
else
for(int q=i; q<=m; q++)
d[k++]=c[q];
}
void MergePass(int *x,int *y,int s,int n)
// 将x中长度为s的相邻子数组合并,结果存入y中
// n: 数组元素数量
{
int i=0; //之数组起始位置
while(i+2*s<=n) {
Merge(x,y,i,i+s-1,i+2*s-1);
i+=2*s;
}
if(i+s
//二路归并排序算法
#include
#include
void disp(int a[], int n) //输出a中所有元素
{
int i;
for(i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
}
void Merge(int a[], int low, int mid, int high)
//将a[low..mid]和a[mid+1..high]两个相邻的有序子序列归并为一个有序子序列a[low..high]
{
int *tmpa;
int i = low, j = mid + 1, k = 0; //k是tmpa的下标,i、j分别为两个子表的下标
tmpa = (int *)malloc((high - low + 1) * sizeof(int)); //动态分配空间
while(i <= mid && j <= high) //在第1子表和第2子表均未扫描完时循环
if(a[i] <= a[j]) { //将第1子表中的元素放入tmpa中
tmpa[k] = a[i];
i++;
k++;
}
else { //将第2子表中的元素放入tmpa中
tmpa[k] = a[j];
j++;
k++;
}
while(i <= mid) { //将第1子表余下部分复制到tmpa
tmpa[k] = a[i];
i++;
k++;
}
while(j <= high) { //将第2子表余下部分复制到tmpa
tmpa[k] = a[j];
j++;
k++;
}
for(k = 0, i = low; i <= high; k++, i++) //将tmpa复制回a中
a[i] = tmpa[k];
free(tmpa); //释放tmpa所占内存空间
}
void MergePass(int a[], int length, int n) //一趟二路归并排序;合并排好序的相邻数组段
{
int i;
for(i = 0; i + 2 * length - 1 < n; i = i + 2 * length) //归并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 MergeSort(int a[], int n) //二路归并算法
{
int length;
for(length = 1; length < n; length = 2 * length)
MergePass(a, length, n);
}
int main()
{
int n = 11;
int a[] = {2, 5, 11, 7, 10, 6, 9, 4, 3, 8, 1};
printf("排序前: ");
disp(a, n);
MergeSort(a, n);
printf("排序后: ");
disp(a, n);
}
自然合并排序是合并排序算法MergeSort的一个变形。
在上述合并排序算法中,第一步合并相邻长度为1的子数组段,这是因为长度为1的子数组段是已排好序的。事实上,对于初始给定的数组a,通常存在多个长度大于1的已自然排好序的子数组段。
例如,若数组a中元素为{4,8,3,7,1,5,6,2},则自然排好序的子数组段有{4,8},{3,7},{1,5,6}和{2}。用1次对数组a的线性扫描就足以找出所有这些排好序的子数组段。然后将相邻的排好序的子数组段两两合并,构成更大的排好序的子数组段。对上面的例子,经一次合并后可得到2个合并后的子数组段{3,4,7,8}和{1,2,5,6}。继续合并相邻排好序的子数组段,直至整个数组已排好序。这2个数组段再合并后就得到{1,2,3,4,5,6,7,8}。
//自然合并排序
#include
#include
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
template
void Merge(Type c[], Type d[], int l, int m, int r)
// 合并c[l:m]和c[m+1:r]到d[l:r]
{
int i=l; // 左子数组的当前处理索引
int j=m+1; // 右子数组的当前处理索引
int k=l; // 合并结果索引
while (i<=m && j<=r)
{
if (c[i]<=c[j]) d[k++]=c[i++];
else d[k++]=c[j++];
}
if (i>m) for(int q=j; q<=r; q++) d[k++]=c[q];
else for(int q=i; q<=m; q++) d[k++]=c[q];
}
template
void NaturalMergePass(
Type x[], Type y[], int seg_i0[], int s, int nseg)
// 将x中长度为s的相邻子段组合并,结果存入y中
// seg_i0: 每个子段的起始索引
// nseg: 子段数量
{
int i=0; // 子段组起始位置
// 合并长度为s的相邻子段组
while (i+s+s<=nseg)
{
Merge(x, y, seg_i0[i], seg_i0[i+s]-1, seg_i0[i+s+s]-1);
i+=s+s;
}
// 剩下子段数量少于2s
if (i+s
void NaturalMergeSort(Type a[], int n)
{
// 寻找子段起始位置和子段数量
int *seg_i0=new int [n+1];
seg_i0[0]=0;
int nseg=1;
for (int i=1; i
void OutputArray(Type elements[], int num_elements)
{
cout<
最坏时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
辅助空间:O(n)