利用归并排序求有限序列的逆序数

来源:互联网 发布:淘宝最低折扣在哪里改 编辑:程序博客网 时间:2024/05/16 13:51

利用归并排序求有限序列的逆序数

逆序数对:在一个序列A={a1, a2, … , an}中,满足i<j, ai>aj­­的一对元素称为一个逆序数对。

那么,怎么求出一个序列的逆序数对的个数呢?

最简单的方法就是顺序扫描序列,求出每个元素的逆序数对,累加即可,时间复杂度是O(n2)。下面我介绍一种时间复杂度是O(nlgn)(注:lgn=log2n)的算法,它由归并排序修改而来。

在归并排序的合并过程中,左半部分元素的位置在原序列中的位置总是小于右半部分的元素,而且各个元素的相对位置在合并之前不会改变,因此利用合并的过程就可以计算出逆序数对的个数。具体做法如下,当左半部分的某个元素ai被合并时,那么右半部分中与ai比较的那个元素设为bj,则右半部分bj之前的那些元素肯定比ai小,因此形成j个逆序数对,因此总逆序数对累加j,当归并排序完成时,即可得出结果。

程序如下:

#include <iostream>using namespace std;//合并有序序列A[p,...,q]和A[q+1,...,r],求取A[p,...,r]区间内的逆序数int update(int *A, int p, int q, int r){int *merge = new int[r-p+1];if (!merge)return 0;    int i=p, j=q+1, k=0, c=0;    while(i<=q && j<=r)    {        if(A[i] <= A[j])        {merge[k++] = A[i++];            c += j-q-1; //A[i]大于A[q+1,...,j-1],因此c增加j-q-1        }        else        {merge[k++] = A[j++];        }    }    while(i<=q)//左半部分的元素没有合并完,则继续更新c    {merge[k++] = A[i++];        c += j-q-1; //A[i]大于A[q+1,...,j-1],因此c增加j-q-1    }while(j<=r)merge[k++] = A[j++];k=0;while(p<=r){A[p++] = merge[k++];}delete []merge;return c;}//求A中p~r区间里逆序数对的个数,用参数c返回结果int  nixushu(int *A, int p, int r){int count = 0;    if(p < r)    {        int q = (p+r)/2;        count += nixushu(A, p, q);        count += nixushu(A, q+1, r);        count += update(A, p, q, r);    }return count;}int nixushu(const int *A, int n){int *copyA = new int[n];if (!copyA)return 0;for (int i=0; i<n; i++)copyA[i] = A[i];int count = nixushu(copyA, 0, n-1);delete []copyA;return count;}int main(){int A1[] = {0, 1, 2, 3, 4, 5, 6};int A2[] = {6, 5, 4, 3, 2, 1, 0};int A3[] = {4, 5, 2, 5, 6, 1, 7, 1};int c1 = nixushu(A1, 7);int c2 = nixushu(A2, 7);int c3 = nixushu(A3, 8);cout <<"A1的逆序数:" << c1 << endl;cout <<"A2的逆序数:" << c2 << endl;cout <<"A3的逆序数:" << c3 << endl;return 0;}