两种方法求解逆序对

逆序对定义:对于一个包含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




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