分治算法之求逆序对数

来源:互联网 发布:渭南广电网络客服电话 编辑:程序博客网 时间:2024/06/06 10:42

问题:有一实数序列a1,a2…an,若i < j 且 ai > aj,则(ai,aj)构成了一个逆序对,请使用分治方法求整个序列中的逆序对的个数,并分析算法的时间复杂度。
答:这个问题最简单的方法就是遍历整个序列,判断实数与它后面的每个实数是否构成逆序对,这种算法的时间复杂度为Ο(n2)。
如果用分治方法求出逆序对数,这就类似与归并排序算法。先将数组从中间分成两个部分,然后分别递归左半部分和右半部分,再合并排好序的左右两个部分,从而统计逆序对数。比如合并两个排好序的数组{1,4,5,7}和{2,3},
1. 先比较后取出1,再比较发现2小于4,那么2肯定小于4后面的元素,从而构成了(4,2),(5,2),(7,2) 3个逆序对。
2. 取出2后,再比较4和3,3<4,所以3和4以及后面的元素都构成了逆序对,共3对。
利用这个方法将数组的左半边和右半边递归分解,在合并过程中统计逆序对的个数。

代码:#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int n=0;//全局变量,用于统计逆序对数
void merge(int a[],int first,int mid,int last)
{
int *temp = new int[last-first+1];//临时数组,用于临时存放比较后的数字
int i=first,j=mid+1,k=0;
while(i<=mid&&j<=last)//遍历比较左右两个部分
{
if(a[i]<=a[j])
temp[k++] = a[i++]; //左半部分元素小于右半部分的元素,将左边该元素存入临时数组
else
{
temp[k++] = a[j++];
n=n+(mid-i+1);//统计左半边能和右半边该元素构成的逆序对数
}
}
while(i<=mid)
temp[k++]=a[i++];
while(j<=last)
temp[k++]=a[j++];
for(i=0; i < k;i++)
a[first + i] = temp[i];//从临时数组取出放回原数组
}
void mergesort(int a[],int first,int last)
{
if(first < last)
{
int mid = (first+last)/2;
mergesort(a,first,mid);//递归排序左半部分
mergesort(a,mid+1,last);//递归排序右半部分
merge(a,first,mid,last);//将处理后的两个部分合并
}
}
int main(int argc, char** argv) {
int a[6] = {6,5,4,3,2,1},i;
cout<<"序列:";
for(i=0;i<6;i++)
cout<< a[i] <<" ";
cout << endl;
mergesort(a,0,5);
cout << endl<< "逆序对数:" << n << endl;
return 0;}

运行结果:

时间复杂度分析:
每次都要将序列的的n个元素合并,时间复杂度为O(n),由于每次都将数组分成两部分,所以一共分了log2n次,所以,该算法时间复杂度为O(n* log2n).

0 0
原创粉丝点击