【leetcode】-计算逆序对(leetcode51)

计算逆序对

假设A[1...n]是一个有n个不一样数的数组,若是i<j,且A[i]>A[j],则(i,j)组成一个逆序对,计算数组A中包含的逆序对的数量ios

  • 示例:
示例 1:

输入: [7,5,6,4]

输出: 5
  • 思路:暴力求解的时间复杂度为O(n2),而采用归并排序的方法能够将复杂度将为O(nlgn),即递归的对分开的子序列分别计算逆序对,在合并时计算两个子序列之间的逆序对,最终结果为左侧子序列的逆序对数量+右侧子序列的逆序对数量+合并左右两侧子序列获得的逆序对数量。数组

    • 统计合并子序列的逆序对数量:在归并排序的过程当中,若是当前左侧元素小于当前右侧元素,则不产生逆序对,仅使用当前左侧元素更新数组便可。若是当前左侧元素大于当前右侧元素,则产生逆序对,逆序对数量为当前左侧剩余元素数,另外使用当前右侧元素更新数组。假设左侧子序列共有五个元素,右侧子序列也有五个元素,当前比较的是左侧的第一个元素和右侧的第一个元素,若是左侧元素大于右侧元素,因为左侧元素已排序(升序),则左侧的全部五个元素都大于右侧的第一个元素,即产生五个逆序对。即当前左侧子序列剩余元素数。
  • 伪码:伪码中数组下标从1开始计算,而不是0优化

    1. 伪码中使用哨兵位来标记数组已结束,其中MAX为大于全部int值的值
    MERGE_INVERSIONS(A,left,mid,right)
        length1=mid-left+1
        length2=right-mid
        //申请两个新数组,长度分别为length1+1和length2+1
        //+1是为了存储哨兵位
        L[1...length1+1]
        R[1...length2+1]
        for i=0 to length1
            L[i] = A[left+i]
        for j=0 to length2
            R[j] = A[mid+j]
        L[length1+1]=MAX
        R[length2+1]=MAX
        i=1
        j=1
        inversions=0
        for n=left to right
            if L[i]<R[j]
                A[n]=L[i]
                ++i
            else
                A[n]=R[j]
                inversions=inversions+length1-i+1
                ++j
        return inversions
    
    COUNT_INVERSIONS(A,left,right)
        if right>left
            mid = (left+right)/2
            leftInversions = COUNT_INVERSIONS(A,left,mid)
            rightInversions=COUNT_INVERSIONS(A,mid+1,right)
            return MEGRE_INVERSIONS(A,left,mid,right)+leftInversions+rightOnbersions
  • 源码实现1:上述伪码的对应实现,其中为了保证哨兵位大于全部int值,用long long来存储数组spa

    #include <iostream>
    #include <limits.h>
    using namespace std;
    
    int merge_inversions(int A[],int left,int mid,int right)
    {
        int result = 0;
        int length1 = mid-left+1;
        int length2 = right - mid; 
        long long *L = new long long[length1 + 1]();
        long long *R = new long long[length2 + 1]();
        for(int n = 0;n<length1;++n)
        {
            L[n] = A[left+n];
        }
        for(int m = 0;m<length2;++m)
        {
            R[m] = A[mid+m+1];
        }
        L[length1] = LLONG_MAX;
        R[length2] = LLONG_MAX;
        for(int n1=0,n2=0,i = left;i<right+1;++i)
        {
            if(L[n1]<=R[n2])
            {
                A[i] = L[n1];
                ++n1;
            }
            else
            {
                A[i] = R[n2];
                result = result + length1 - n1;
                ++n2;
            }
        }
        delete [] L;
        delete [] R;
        return result;
    }
    
    int count_inversions(int *A,int left,int right)
    {
        if(left<right)
        {
            int mid = (left+right)/2;
            int resultLeft = count_inversions(A,left,mid);
            int resultRgiht = count_inversions(A,mid+1,right);
            return merge_inversions(A,left,mid,right) + resultLeft + resultRgiht;
        }
        
        return 0;
    }
    
    int main()
    {
        int test[10]{10,9,8,7,6,5,4,3,2,1};
        cout<<count_inversions(test,0,sizeof(test)/sizeof(int)-1)<<endl;
        
        for(auto n :test)
        {
            cout<<n<<ends;
        }
        
        system("pause");
        return 0;
    }
  • 源码实现2:code

    1. 优化1:源码实现1在每次合并时申请空间,为了不这一状况,只在开始时申请一次内存,反复使用
    2. 优化2:源码实现1中为了保证哨兵位大于全部int值使用了long long,这一操做浪费了空间,所以使用下标控制循环,再也不使用哨兵位,能够节省空间,申请int空间便可
    #include <iostream>
    using namespace std;
    int merge(int *nums,int *data,int left,int mid,int right)
    {       
        int length1 = mid - left+1;
        int length2 = right -mid;
        for(int n = 0;n<length1;++n)
        {
            data[n] = nums[left+n];
        }
        for(int m = 0;m<length2;++m)
        {
            data[m+length1] = nums[mid+m+1];
        }
        int result = 0;
        int n1=0,n2=length1;
        int index = left;
        while(n1<length1&&n2<length2+length1)
        //n2的起始值为length1,共有length2个元素,所以终止值为length2+length1
        {
            if(data[n1]<=data[n2])
            {
                nums[index] = data[n1];
                ++n1;
            }
            else
            {
                nums[index] = data[n2];
                result = result + length1-n1;
                ++n2;
            }
            ++index;
        }
        //将剩余元素放到数组中
        while(n1<length1)
        {
            nums[index] = data[n1];
            ++n1;
            ++index;
        }
        while(n2<length2+length1)
        {
            nums[index] = data[n2];
            ++n2;
            ++index;
        }
        return result;
    }
    
    int count(int *nums,int *data,int left,int right)
    {
        if(right>left)
        {
            int mid = (right+left)/2;
            int leftResult = count(nums,data,left,mid);
            int rightResult = count(nums,data,mid+1,right);
            return merge(nums,data,left,mid,right) + leftResult + rightResult;
        }
        return 0;
    }
    
    int main()
    {
        int nums[10]{10,9,8,7,6,5,4,3,2,1};
        int *data = new int[10]();
        int result = count(nums,data,0,sizeof(nums)/sizeof(int)-1);
        for(auto n:nums)
        {
            cout<<n<<ends;
        }
        delete []data;
        cout<<endl<<"result:"<<result;
        system("pause");
        return 0;
    }
相关文章
相关标签/搜索