分割と征服のための逆順vsマージソート方法

リバースペア紹介

1≤i <j≤nおよびA [i]> A [j]である正の整数i、jがある場合、<A [i]、A [j]>の順序付けられたペアはAの逆ペアと呼ばれ、逆注文番号と呼ばれる。

配列内の逆ペアの数を解く

実際、小さな添え字で多数を見つけることなので、まず暴力について話そうではありません。暴力は明らかにO(n 2)、8 Taihangです。

int unseq(vector<int> &nums)
{
	int cnt = 0;
	for(int i=0; i<nums.size(); i++)
		for(int j=i+1; j<nums.size(); j++)
			if(nums[i]>nums[j]) cnt++;
	return cnt;
}

分割して征服する思考:マージで並べ替え

配列内の逆順のペアの数については、配列を[l, mid]AND [mid+1, r]間隔に分割すると、問題はサブ問題に分解できます。

  • サブアレイの[l, mid]逆順ペア番号lcnt
  • サブ配列[mid+1, r]rcntのペア番号の逆順
  • A [i]は左側にあり、A [j]は右側にあり、A [i]>の<i、j>のペア> A [j]

質問1と2は再帰的に解決できます。次に重要なのは、質問3を見つける方法です。つまり、

ここで問題12を解決する場合、解法だけでなく配列もソートされます。つまり、左側/右側が順序付けられた状態にあり、O(n)時間を使用して解を完了することができます。

  • 右側のサブ配列で、jの添え字を後ろから前に列挙します
  • 左側のサブ配列で、ポインターiを設定します。最初はiが中央を指します。
  • i左にスワイプして、A [i] <= A [j]となる最初のiを見つけます
  • 説明上記i+1 ~ mid添字を、A [i]には、A [j]がより大きい
  • A [j]は右に逆にその後のために、見つかったmid-i

両側が順序付けられているため、j添え字の場合、A [j]は減少しているため、さらにA[j+1]配对的个数,同样可以运用到A[j]増加し、さらに、A [j]がより多くの逆順のペアを満たすことができるかどうかをさらに判断する必要があります

ここでは、配列の両側の順序が使用され、以前の結果は時間を節約するために偽装して使用されます

それは二重ループのようです、実際には、i、jはn / 2回以上戻らないため、複雑さはO(n)です。

O(n)時間を使用して逆の順序でペアの数を見つけると、以前の再帰を容易にして配列をマージして検索を続行する必要があります

マージ操作には[ inplace_merge関数 ]を使用します

ここに多くの疑問符があるかもしれません:

仕分け後、注文が乱れていませんか?

順序を入れ替える(ソートする)前に、両側のサブ配列の解の数を取得しまし
た。十字の逆のペアを解くと、左と右がソートされますが、それらの相対位置は変更されません。つまり、左の要素は左側のサブアレイ、右側も同じ

int unseq(vector<int> &nums, int l, int r)
{
	if(l>=r || l<0 || r>=nums.size()) return 0;
	int mid=(l+r)/2, cnt=0, i=mid;
	int lcnt = unseq(nums, l, mid);
	int rcnt = unseq(nums, mid+1, r); 
	for(int j=r; j>mid; j--)
	{
		while(i>=l && nums[j]<nums[i]) i--;
		cnt += mid-i;
	}
	inplace_merge(nums.begin()+l, nums.begin()+mid+1, nums.begin()+r+1);
	return lcnt+rcnt+cnt;
}

完全なコード

#include <bits/stdc++.h>

using namespace std;

int unseq(vector<int> &nums, int l, int r)
{
	if(l>=r || l<0 || r>=nums.size()) return 0;
	int mid=(l+r)/2, cnt=0, i=mid;
	int lcnt = unseq(nums, l, mid);
	int rcnt = unseq(nums, mid+1, r); 
	for(int j=r; j>mid; j--)
	{
		while(i>=l && nums[j]<nums[i]) i--;
		cnt += mid-i;
	}
	inplace_merge(nums.begin()+l, nums.begin()+mid+1, nums.begin()+r+1);
	return lcnt+rcnt+cnt;
}

int unseq(vector<int> &nums)
{
	int cnt = 0;
	for(int i=0; i<nums.size(); i++)
		for(int j=i+1; j<nums.size(); j++)
			if(nums[i]>nums[j]) cnt++;
	return cnt;
}

int main()
{	
	vector<int> nums{2,7,4,3,1,8,6,5};
	cout<<unseq(nums)<<endl;
	cout<<unseq(nums, 0, nums.size()-1)<<endl;
	for(int i=0; i<nums.size(); i++) cout<<nums[i]<<" ";
	cout<<endl;
	
	return 0;
}
公開された262元の記事 ウォン称賛11 ビュー10000 +

おすすめ

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