归并排序以及逆序对统计
1. 归并排序
归并排序利用分治的方法,将两个有序数组进行合并,达到排目的。有序数组可以通过不停地将数组进行二分,最终得到一个数,认为此数组有序。然后将两个一个数的数组进行合并,得到一个有序的有两个数据的数组,然后返回上一层继续合并,最终得到有序数列。
第一步:解决两个有序数组合并
void mergeArray(int a[],int m, int b[], int n, int re[]) //a,b,合并到re;m,n分别为a,b数组的长度
{
int i,j,k; //给两个数组分别设定一个指针,把两个之中较小的放在re中,本次较小的指针后移
i=j=k=0;
while(i<m && j<n) //合并相同长度的数据
{
if(a[i]>b[j])
re[k++]=b[j++];
else
re[k++]=a[i++];
}
while(i<m) //多出的部分直接复制在re中
re[k++]=a[i++];
while(j<n) //多出的部分直接复制在re中
re[k++]=b[j++];
}
第二步:归并排序
void mergeArray(int a[],int left,int mid,int right, int tmp[])
//合并a[left...mid],a[mid+1...right]
{
int i=left,j=mid+1,k=0;
int m=mid,n=right;
while(i<=m && j<=n)
{
if(a[i]>a[j])
tmp[k++]=a[j++];
else
tmp[k++]=a[i++];
}
while(i<=m)
tmp[k++]=a[i++];
while(j<=n)
tmp[k++]=a[j++];
for(i=0;i<k;i++)
//本层合并完两个有序数组,返回到上一层位置,作为上一层的有序数组的基础,进而进行上一组的合并操作
a[left+i]=tmp[i];
}
void mergeMain(int a[],int left, int right, int temp[])
{
int mid;
if(left<right)
{
mid=left+(right-left)/2;
mergeMain(a,left,mid,temp); //左边递归二分数组
mergeMain(a,mid+1,right,temp); //右边递归二分数组
mergeArray(a,left,mid,right,temp); //合并操作
}
}
bool mergeSort(int a[],int n)
{
int *re;
re=new int[n]; //一次性分配好空间,不能每次分配,释放
if(!re) //检查空间是否分配成功
return false;
mergeMain(a,0,n-1,re);
delete[] re;
re=NULL; //空指针
return true;
}
2. 逆序对
逆序对数量查找可以通过归并排序的交换次数算出,在数组
a[left...mid]和a[mid+1..right],left<= i <=mid, mid+1<= j< =right
,当满足a[i] > a[j]
时存在逆序对,假设a[j]放在a[i]之前,则a[i]到a[mid]
(因为a数组为有序数组)的值都大于a[j],共有mid-i+1
个逆序对,因此只需要在归并排序中合并数组的时候稍加改动即可
int ans=0; //全局变量统计数目
void mergeArray(int a[],int left,int mid,int right, int tmp[])
{
int i=left,j=mid+1,k=0;
int m=mid,n=right;
while(i<=m && j<=n)
{
if(a[i]>a[j])
{
tmp[k++]=a[j++];
//当a[i]>a[j]的时候,如果此时把a[j]放在a[i]前面,那么a[i]到a[mid]的值都大于a[j],因此在交换之前属于逆序对
//左边数组 9 8 7 右边数组 1 2 3
//当把 1 放在 9 前面,那么9 8 7 都是 1 的逆序对,数量 mid-1+1
ans+=mid-i+1;
}
else
tmp[k++]=a[i++];
}
while(i<=m)
tmp[k++]=a[i++];
while(j<=n)
tmp[k++]=a[j++];
for(i=0;i<k;i++)
a[left+i]=tmp[i];
}