归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
算法思想(升序排列):
设对于无序序列含n个元素的无序序列array,共需lg(n)+1趟归并。
第1趟归并后n个元素产生n/2个文件(即n/2次归并),每个文件含有2个元素;第2趟归并后你n/2个文件产生n/4个文件,每个文件含有4个元素;……
每一趟归并时对相邻的两个文件进行比较,将文件中较小元素存入额外申请的序列,并对该文件做自减操作,直到某个文件中元素为0个,最后将含有剩余元素的文件整体插入到额外序列的末尾。
每一趟归并中使用三指针:left指向某个文件的第一个元素,mid只向该文件的最后一个元素(即两个文件的中间元素),right只向该文件后面文件的最后一个元素。
最后一趟归并后只剩余一个文件,共含n个元素。
需要注意的是, 对于每一趟归并:
1、如果所含的文件个数为奇数,则漏过最后一个文件,不对其归并
2、如果所含的文件个数为偶数,则该趟归并的最后两个文件中的right指针要只向n,即最后一个元素(在程序处理时可在循环中先漏过最后两个文件,在循环之外单独处理)
程序(C++):
#include <iostream>
#include <string>
using namespace std;
void merge_sort(int v[],int left,int right,int mid){
int *arr=new int[right-left+1];
int i=left,j=mid+1;
int k=0;
while(i<=mid&&j<=right){
if(v[i]<v[j]){ arr[k]=v[i];i++; }
else{ arr[k]=v[j];j++; }
k++;
}
while(i<=mid){
arr[k]=v[i];i++;k++;
}
while(j<=right){
arr[k]=v[j];j++;k++;
}
k=0;
for(i=left;i<=right;i++){
v[i]=arr[k];k++;
}
delete []arr;
}
void merge(int v[],int n){
int files=n,i=1,left,right,mid,every=1;
while(files>1){ //如果文件数大于1,继续归并
if(files%2!=0){ //如果文件数为奇数,只比较前files-1个文件,
left=0;right=2*every-1;mid=(left+right)/2;
for(int j=0;j<(files-1)/2;j++){ //前files-1个文件共需比较(files-1)/2次
merge_sort(v,left,right,mid);
left=left+2*every;right=right+2*every;mid=(left+right)/2;
}
}else{ //如果文件数为偶数,共需比较files/2次
left=0;right=2*every-1;mid=(left+right)/2;
for(int j=0;j<(files/2)-1;j++){
merge_sort(v,left,right,mid);
left=left+2*every;right=right+2*every;mid=(left+right)/2;
}
//进行该趟最后两个文件的比较
right=n-1;
mid=(files-1)*every-1;
left=mid-every+1;
merge_sort(v,left,right,mid);
}
files=files/2+files%2; //计算每趟归并后产生的文件数
every=2*every; //计算每个文件中含有元素的个数
}
}
int main(){
Int a[10]={9,8,7,6,5,4,3,2,1,0};
merge(a,10);
for(int i=0;i<n;i++)
cout<<a[i]<<' ';
return 0;
}