计算数组中的逆序对

有一个由N个实数构成的数组,如果一对元素A[i]和A[j]是倒序的,即i<j但是A[i]>A[j]则称它们是一个倒置,设计一个计算该数组中所有倒置数量的算法。要求算法复杂度为O(nlogn)

暴力求解

思路:
循环从数组中取出一个元素k,然后从k之后的元素中找到比k小的元素个数,最后统计所有的个数即为排列中逆序对的数目。

时间复杂度: O(n^2)

修改归并排序

前提知识:归并排序
归并排序是分治算法一个重要的应用。
讲解及代码:https://www.cnblogs.com/skywang12345/p/3602369.html#a42

思路:
根据归并排序的基本思想,我们来到计算逆序对。
在归并排序的合并操作时,数组被划分左右两部分,此时假设左部分的逆序对有a个 ,右边有b个,如果左边存在一个数a[i]大于右边的数a[j],由于两数组分别是有序的,那么从a[i]到左边结束部分的数字都比a[j]大,即与b[j]有关的逆序对数=(左边部分的end)-i+1 个。
那么我们只需要在每次合并操作时统计这部分个数并求和,仅在merge函数内加一条语句。
(ps:左右两部分内部的分别排序不会影响两部分之间的逆序对数,因为只是内部有序。)

代码

#include <iostream>
using namespace std;

void merge(int a[],int start,int mid,int end,int &num){
    int *tmp=new int[end-start+1];
    int s=start;
    int m=mid+1;
    int e=end;
    int k=0;

    while(s<=mid && m<=e){
        if(a[s]>a[m]){
            tmp[k++]=a[m++];
            num+=mid-s+1;           //逆序数增加 end- i + 1个
        }
        else{
            tmp[k++]=a[s++];
        }
    }

    while(s<=mid)
        tmp[k++]=a[s++];
    while(m<=e)
        tmp[k++]=a[m++];

    for (int i = 0; i < k; i++)
        a[start + i] = tmp[i];

}



void mergeSort(int a[],int start,int end,int &num){
    if(start==end)
        return;

    int mid=(start+end)/2;

    mergeSort(a,start,mid,num);
    mergeSort(a,mid+1,end,num);

    merge(a,start,mid,end,num);

}

int main(){

    int a[]={8,3,2,9,7,1,5,4};
    int num=0;      //存储逆序数组对数

    mergeSort(a,0,7,num);        //7是数组a的长度

    cout<<num<<endl;

    return 0;

}

时间复杂度:O(nlog n)

猜你喜欢

转载自blog.csdn.net/qq_33618962/article/details/85079869