两种方法求解逆序对

来源:互联网 发布:rrt算法 编辑:程序博客网 时间:2024/05/21 19:36

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




0 0