(leetcode 51)

Time:2021-5-4

Calculating reverse order pairs

Suppose a [1… N] is an array with n different numbers, if I < J, and a [i] > a [J], then (I, J) forms a reverse order pair, and calculates the number of reverse order pairs contained in array a

  • Example:

Example 1:

Input: [7,5,6,4]

Output: 5

  • Idea: the time complexity is O (n)2)The complexity can be set to o (nlgn) by merging and sorting, that is, recursively calculating the reverse order pairs of the separated subsequences, and calculating the reverse order pairs between the two subsequences when merging. The final result is the number of reverse order pairs of the left subsequence + the number of reverse order pairs of the right subsequence + the number of reverse order pairs obtained by merging the left and right subsequences.

    • Count the number of reverse order pairs of merged subsequences: in the process of merging and sorting, if the current left element is less than the current right element, no reverse order pair will be generated, and only the current left element will be used to update the array. If the current left element is larger than the current right element, a reverse order pair is generated. The number of reverse order pairs is the number of remaining elements on the current left. In addition, the current right element is used to update the array. Suppose that there are five elements in the left subsequence and five elements in the right subsequence. The current comparison is the first element on the left and the first element on the right. If the left element is larger than the right element, because the left element has been sorted (ascending), all five elements on the left are larger than the first element on the right, that is, five reverse order pairs are generated. That is, the number of remaining elements of the current left subsequence.
  • Pseudo code: the array subscript in pseudo code is calculated from 1 instead of 0

    1. The guard bit is used in the pseudo code to mark the end of the array, where Max is a value greater than all int values
    MERGE_INVERSIONS(A,left,mid,right)
        length1=mid-left+1
        length2=right-mid
        //Apply for two new arrays, length 1 + 1 and length 2 + 1, respectively
        //+1 is to store sentry positions
        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
  • Source code implementation 1: the corresponding implementation of the above pseudo code, in order to ensure that the sentinel bit is greater than all int values, long long is used to store the array

    #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;
    }
  • Source code implementation 2:

    1. Optimization 1: source code implementation 1 applies for space in each merge. In order to avoid this situation, it only applies for memory once at the beginning and uses it repeatedly
    2. Optimization 2: in source code implementation 1, in order to ensure that the sentinel bit is greater than all int values, long long is used, which wastes space. Therefore, subscript control loop is used instead of sentinel bit, which can save space and apply for int space
    #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)
        //The starting value of N2 is length1, and there are two elements in total, so the ending value is length2 + length1
        {
            if(data[n1]<=data[n2])
            {
                nums[index] = data[n1];
                ++n1;
            }
            else
            {
                nums[index] = data[n2];
                result = result + length1-n1;
                ++n2;
            }
            ++index;
        }
        //Put the remaining elements in the array
        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;
    }