Leetcodeはオファー51を指します:配列内の逆ペア-アイデアとコードを分割統治します

序文

前に一度書かれました。

https://blog.csdn.net/weixin_44176696/article/details/105092613

書き方が悪いので、今日は簡単なリメイクに行きます。

タイトル説明

配列内の2つの数値の場合、最初の数値が次の数値より大きい場合、2つの数値は逆のペアを形成します。配列を入力して、この配列のリバースペアの総数を見つけます。

例1:

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

制限:

0 <= 数组长度 <= 50000

アイデア

配列内の逆順序対を見つけます。つまり、2つの添え字(i、j)を見つけます。ここで、i、jの分布は次の3つのケースに分けられます。

  1. i、jは配列の左半分にあります
  2. i、jは配列の右半分にあります
  3. iは配列の左半分にあり、jは配列の右半分にあります

ここに画像の説明を挿入

分割統治法を使用してケース1と2を見つけるのは非常に簡単で、再帰によって実現できます。難しさは主にケース3の解決策、つまりiとjが配列の両方の半分にある場合にあります。強引な列挙が使用されている場合、分割統治の合併コストは依然としてO(n^2)

ただし、マージソートの特性を使用できます。つまり、分割統治後のマージソートを使用すると、配列の左右が順番に増加します。iとjが配列の2つの半分にある状況のみを考慮しているため、それらの順序は重要ではありません。

右側の点jを列挙します。各点jについて、左半分に最初のi添え字が見つかるのでnums[i]<=nums[j]、これは、区間[i + 1、mid]の点がすべて満たされていることを意味しますnums[i]>nums[j]。したがって、右側の場合エンドポイントjには、次のmid-i反転があります。

図に示すように、黄色のボックス内の点xは nums[x]>nums[j]

ここに画像の説明を挿入

さらに、左半分と右半分が単調に増加しているため、列挙jの順序は右から左する必要があります。これは次の理由によるものです。

  • jをn回列挙すると、このとき右端点=jnums[i]>nums[j]
  • n + 1回の列挙j、現時点では、まだ確立されている右端点=j-1ためnums[j-1]<nums[j]nums[i]>nums[j-1]

つまり、前回の結果を再利用して、左端点iの繰り返しの動きを減らします左の点imidから列挙を開始してから、l-1移動回数を超えない位置に移動するmid-lため、合併の費用は依然としてO(n)

コード

class Solution {
    
    
public:
    vector<int> nums;
    int divide(int l, int r)
    {
    
    
        // 边界 -- 长度为1的区间没有逆序对
        if(l<0 || r>=nums.size() || l==r) return 0;

        int ans=0, mid=(l+r)/2;
        ans += divide(l, mid);      // 逆序对两点都在左半边
        ans += divide(mid+1, r);    // 逆序对两点都在右半边

        // 逆序对两点一个在左半边一个在右半边
        int i = mid;
        for(int j=r; j>=mid+1; j--)
        {
    
    
            while(i>=l)
            {
    
    
                if(nums[i]<=nums[j]) break; // 找第一个 nums[i]<=nums[j] 
                i--;
            }
            ans += mid-i;   // [i+1, mid] 区间的数都大于 nums[j]
        }

        // 归并排序
        auto b = nums.begin();
        inplace_merge(b+l, b+mid+1, b+r+1);
        
        return ans;
    }
    int reversePairs(vector<int>& nums) {
    
    
        if(nums.size()==0) return 0;
        this->nums = nums;
        return divide(0, nums.size()-1);
    }
};

おすすめ

転載: blog.csdn.net/weixin_44176696/article/details/109556032