歸並排序以及逆序對統計
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];
}