逆序對的定義如下:對於數列的第 i 個和第 j 個元素,如果滿足 i < j 且 a[i] > a[j],則其爲一個逆序對;否則不是。
怎麼求逆序對呢,我們今天用歸併排序來說,資資給我們講的時候就說歸併排序80%會考。我也不知道考沒考,我緩考了。
emmm,歸併排序大家可能記不太清了,我再給大家提一下。
所謂歸併排序就是將一組數進行無數次分割,將它劃分成一個個有序的區間,然後再將這一個個有序的區間一個個合併成有序的區間。
怎麼劃分?
那這就是一個二分的過程了。我們每次將一段數進行分開,我們在這裏引入一個mid爲中間位置,一直將l到mid進行分,直到mid=l,這說明他已經將一個個連續的區間劃分成一個個獨立的區間了。這樣我們在一個個進行合併的時候由於我們劃分成兩個區間(即兩個兩個區間進行合併),並且每一個區間都是有序的。
實際上歸併排序的交換次數就是這個數組的逆序對個數
我們可以這樣考慮:
歸併排序是將數列a[l,r]分成兩半a[l,mid]和a[mid+1,r]分別進行歸併排序,然後再將這兩半合併起來。
在合併的過程中(設l<=i<=mid,mid+1<=j<=h),當a[i]<=a[j]時,並不產生逆序數;當a[i]>a[j]時,在前半部分中比a[i]大的數都比a[j]大,將a[j]放在a[i]前面的話,逆序數要加上mid+1-i。
因此,可以在歸併排序中的合併過程中計算逆序數。
歸併排序求逆序對其實就是在歸併排序的基礎上加上了一個統計逆序對的過程。
#include <bits/stdc++.h>
using namespace std;
int arr[100010];
int temp[100010];
long long merge_sort(int arr[],int l,int r)
{
if(l >= r)
{
return 0;
}
long long cnt = 0;
int mid = l + r >> 1;
cnt += merge_sort(arr,l,mid);
cnt += merge_sort(arr, mid + 1, r);
int k = 0;
int i = l;
int j = mid + 1;
while(i <= mid && j <= r)
{
if(arr[i] <= arr[j])
{
temp[k++] = arr[i++];
}
else
{
cnt += mid - i + 1;
temp[k++] = arr[j++];
}
}
while(i <= mid)
{
temp[k++] = arr[i++];
}
while(j <= r)
{
temp[k++] = arr[j++];
}
for(i = l,j = 0;i <= r;i++,j++)
{
arr[i] = temp[j];
}
return cnt;
}
int bubble_sort(int arr[], int len) {
int ans = 0;
int i, j;
for (i = 0; i < len - 1; i++) //外層循環控制趟數,總趟數爲len-1
{
for (j = 0; j < len - 1 - i; j++)//內層循環爲當前i趟數 所需要比較的次數
{
if (arr[j] > arr[j + 1])
{
ans++;
swap(arr[j], arr[j + 1]);
}
}
}
return ans;
}
int main()
{
int n;
cin>>n;
for(int i = 0;i < n;i++)
{
cin>>arr[i];
}
cout<<merge_sort(arr,0,n-1)<<endl;
// cout<<bubble_sort(arr,n)<<endl;
// for(int i = 0;i < n;i++)
// {
// cout<<arr[i]<<" ";
// }
return 0;
}