兩種方法求解逆序對

逆序對定義:對於一個包含N個非負整數的數組A[1..n],如果有i < j,且A[ i ]>A[ j ],則稱(A[ i] ,A[ j] )爲數組A中的一個逆序對。


常見的兩種方法求解逆序對:1.窮舉法(暴力求解),時間複雜度O(n^2)。2.歸併法, 時間複雜度O(nlogn)。




窮舉法:對於一個給定的序列,依次從左往右取每一個元素,從該元素右邊第一個元素開始向右掃描,遇到比它小的元素,則計數+1,直到處理完整個序列。


#include <iostream>
#include <cstdlib>
#include <cstdio>

using namespace std;

#define MAXN 1000
int num[MAXN];
int sum = 0;

void solve(int n)
{
    for(int i = 0; i < n; i++)
        for(int j = i+1; j < n; j++)
        {
            if(num[i] > num[j])
                sum++;
        }
}          

int main(int argc, char const *argv[])
{
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
        scanf("%d", &num[i]);
    solve(n);
    printf("%d\n", sum);
    return 0;
}



歸併法:將序列A[l..r]分成兩半A[l..mid]和A[mid+1..r]分別進行歸併排序,然後再將這兩半合併起來。

在合併的過程中(設l<=i<=mid,mid+1<=j<=r),當A[i]<=A[j]時,不產生逆序數;當A[i]>A[j]時,在

前半部分比A[i]大的數都比A[j]大,所以,將A[j]放在A[i]前面的話,逆序數要加上mid+1-i。因此,可以在歸併序中的合併過程中計算逆序數。



#include <iostream>
#include <cstdlib>
#include <cstdio>

using namespace std;

#define MAXN 1000
int num[MAXN], temp[MAXN];
int sum = 0;

void Merge(int l, int mid, int r)
{
    int i = l, j = mid+1, k = l;
    while(i <= mid && j <= r)
    {
        if(num[i] > num[j])
        {
            temp[k++] = num[j++];
            sum += mid-i+1;//產生逆序對
        }
        else
            temp[k++] = num[i++];
    }
    while(i <= mid)
        temp[k++] = num[i++];
    while(j <= r)
        temp[k++] = num[j++];
    for(int i = l; i <= r; i++)
        num[i] = temp[i];

}

void Merge_sort(int l, int r)
{
    if(l < r)
    {
        int mid = l + (r-l)/2;//可防止加法溢出
        Merge_sort(l, mid);//左邊
        Merge_sort(mid+1, r);//右邊
        Merge(l, mid, r);//合併
    }

}
     

int main(int argc, char const *argv[])
{
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
        scanf("%d", &num[i]);
    Merge_sort(0, n-1);
    printf("%d\n", sum);
    return 0;
}





附上POJ逆序對的一道測試題:http://poj.org/problem?id=1804




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章