【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;
    }
相關文章
相關標籤/搜索