一个较为复杂的二路归并算法
/*
作者:bacoo
日期:2008年9月21日
一个比较复杂的二路归并排序算法,完成由小到大的排序功能
算法摒除了最初始的“从两路头部依次取出数据,经过比较后输出较小值”方法;
而是找出“头大”的一路,然后把另一路分成许多片断,然后插入到“头大”的那路中,
这里面寻找插入点使用了二分法查找以提高效率,
需要说明的是,该算法在两部“交错”不是很严重的情况下有较好的效率。
*/
#include <iostream>
using namespace std;
int find2fenfa(int a[],int h,int t,int num){//利用二分法来查找num的位置,要求在[h,t]之间
while(h<t){
int m=(h+t)/2;
if(num==a[m]){
return m;
}
else if(num<a[m])
t=m-1;
else
h=m+1;
}
return h;//这只给出一个推荐位置,也即最相关位置,在后续应用该结果时,需要做分析处理
}
void movedata(int a[],int pos,int h,int t){//该函数用于将[h,t]区间内的数据移动到pos指定的插入位置
int s;
if(pos>t){//插入点在后方
for(int i=t;i>=h;i--){
s=a[i];
for(int j=i;j<pos-1;j++){
a[j]=a[j+1];
}
a[--pos]=s;
}
}
else if(pos<h){//插入点在前方
for(int i=h;i<=t;i++){
s=a[i];
for(int j=i;j>pos;j--){
a[j]=a[j-1];
}
a[pos++]=s;
}
}
}
void merge2(int a[],int h1,int t1,int h2,int t2)//该函数用于两路合并,h和t分别表示头部和尾部
{
if( h1>t1 || h2>t2 )return;
int i_pos=-1,s_end=-1;
if(a[h1]<=a[h2]){//确保都是为一个大数查找插入的位置
i_pos=find2fenfa(a,h1,t1,a[h2]);
a[i_pos]<=a[h2] ? i_pos++ : i_pos;//根据推荐位置对应的数值大小来调整合适的插入点
if(i_pos>t1)return;//已经有序,不必再合并了
s_end=find2fenfa(a,h2,t2,a[i_pos]);//为待插入的部分找到一个终点,起始点已经由h2来标记了
a[s_end]>=a[i_pos] ? s_end-- : s_end;//微调,很必要的
movedata(a,i_pos,h2,s_end);
merge2(a,i_pos+s_end-h2,s_end,s_end+1,t2);
}else{
i_pos=find2fenfa(a,h2,t2,a[h1]);
a[i_pos]<=a[h1] ? i_pos++ : i_pos;
if(i_pos>t2){
movedata(a,h1,h2,t2);
return;
}
s_end=find2fenfa(a,h1,t1,a[i_pos]);
a[s_end]>=a[i_pos] ? s_end-- : s_end;
movedata(a,i_pos,h1,s_end);
merge2(a,h1,t1-(s_end-h1+1),t1-s_end+h1,t2);
}
}
void sort_2way(int a[],int len){//两路归并,这个函数主要是为merge2函数每次分配合适的两路
int x=1;//表示当前进行的两路归并中的“路的长度”
int h1,t1,h2,t2;
while(x<=len){
for(int i=0;i<len;i+=2*x){
h1=i;
h2=i+x;
t1=h2-1;
t2=h2+x-1;
if(h2>=len)continue;
else{
if(t2>=len){
merge2(a,h1,t1,h2,len-1);
break;
}else
merge2(a,h1,t1,h2,t2);
}
}
x*=2;
}
}
void main(){
int a[10];
int len=sizeof(a)/sizeof(a[0]);
for(int i=0;i<len;i++)a[i]=rand()%100;
sort_2way(a,len);
for(i=0;i<len;i++)cout<<a[i]<<endl;
}
单次运行结果为:
0
24
34
41
58
62
64
67
69
78
Press any key to continue