[实验目的]
基本掌握分治算法的原理.
掌握二路归并排序的算法及递归程序的设计.
【问题描述】 给定一个整数数组A=(a0,a1,…,an-1)。若 i<j 且 ai>aj,则<ai, aj>就是一个逆序对。例如数组(3,1,4,5,2)中,含有4个逆序对。编写一个程序,采用分治法中的二路归并排序算法,递归地求解A中的逆序对的个数,即逆序数。
【提示】采用分治法中的二路归并排序算法,对数组进行排序,在归并各个子序列时,统计逆序对的个数,如下图所示:
参考代码为:
/*****求字符串a的逆序数ans***************/
int ans; //全局变量,累计逆序数
void Merge(int a[],int low,int mid,int high) //两个相邻有序段归并
{ int i=low;
int j=mid+1;
int k=0;
int *tmp=(char *)malloc((high-low+1)*sizeof(int));
while(i<=mid && j<=high) //二路归并
{ if(a[i]>a[j])
{ tmp[k++]=a[j++];
ans+=mid-i+1;
}
else tmp[k++]=a[i++];
}
while(i<=mid) tmp[k++]=a[i++];
while(j<=high) tmp[k++]=a[j++];
for(int k1=0;k1<k;k1++) //tmp[0..k-1]=>a[low..high]
a[low+k1]=tmp[k1];
free(tmp);
}
【算法时间复杂度分析】
设整个算法的执行时间为T(n),显然Merge(a,0,n/2,n-1)的执行时间为O(n),所以得到以下递推式:
T(n)=1 当n=1
T(n)=2T(n/2)+O(n) 当n>1
【思考】如果有多组长度相同的整数数列,每一组都求解逆序对数,然后,按“最多逆序数”到“最少逆序数”的顺序输出所有数列。若两个数列的逆序对个数相同,按原始顺序输出它们,该如何修改程序,完成此问题?
符合题意的算法如下:
#include<iostream>
#define MAXN 55
#define MAXM 105
using namespace std;
int ans;
void Merge(char a[],int low,int mid,int high){
int i=low;
int j=mid+1;
int k=0;
char *tmp=(char *)malloc((high-low+1)*sizeof(int));
while(i<=mid && j<=high){
if(a[i]>a[j]){
tmp[k++]=a[j++];
ans+=mid-i+1;
}
else tmp[k++]=a[i++];
}
while(i<=mid) tmp[k++]=a[i++];
while(j<=high) tmp[k++]=a[j++];
for(int k1=0;k1<k;k1++)
a[low+k1]=tmp[k1];
free(tmp);
}
void Merge_sort(char a[],int low,int high){
if(low<high){
int mid=(low+high)/2;
Merge_sort(a,low,mid);
Merge_sort(a,mid+1,high);
Merge(a,low,mid,high);
}
}
int inversion(char a[],int n){
ans=0;
Merge_sort(a,0,n-1);
return ans;
}
int main(){
char a[MAXM];
int n,number;
cout<<"please input the number of the content:"<<endl;
cin>>n;
cout<<"please input the content:"<<endl;
cin>>a;
number=inversion(a,n);
cout<<"逆序数的对数是:"<<number<<endl;
return 0;
}