債務返済質問3
タイトル説明
ソリューションマージソート
解決策を検討していなければ、現在、どのレベルでもマージと並べ替えを使用することは考えられません。
マージとソートのプロセスは、分解してからマージすることです。マージソート
は、分割統治のアイデアに基づいたソートです。
そして、治療の過程で逆順序対を数えることができます。
古いステッチは変です。怠惰な犬。
したがって、唯一の難しさは、ペアの数を逆の順序で数える方法です。
マージ関数で、左側と右側をマージするとき
に、左側の配列に右側の配列の数値aよりも大きい数値bがあることがわかった場合
、これはすべての数値と逆のペアを形成できます。 bから左配列の左端まで。
この時点で左側の配列はすでに順序付けられている(増加している)ため、添え字から境界までのすべての数値はaより大きくなります。
境界がmidaで、添え字がiの
場合、形成できる逆ペアの数はmid-i +1です。
class Solution {
public:
int reversePairs(vector<int>& nums) {
if(nums.size() < 2) return 0;
int len = nums.size();
vector<int> tmp(len);
return mergeSort(nums, 0, len - 1, tmp);
}
int mergeSort(vector<int>& nums, int left, int right, vector<int> & tmp) {
//递归结束条件
if(left >= right) return 0;
int mid = left + (right - left) / 2;
int leftPairs = mergeSort(nums, left, mid, tmp);
int rightPairs = mergeSort(nums, mid + 1, right, tmp);
if(nums[mid] <= nums[mid + 1]) {
//当我们调用归并之后,左右部分都已经排序好
//只需要判断左边的最大值是否小于右边的最小值
//如果小于就不存在跨左边和右边的逆序对了
return leftPairs + rightPairs;
}
int crossPairs = merge(nums, left, mid, right, tmp);
return leftPairs + rightPairs + crossPairs;
}
int merge(vector<int> & nums, int left, int mid, int right, vector<int>& tmp) {
//归并排序开启条件:左右部分都已经升序排序好
//进行左右的归并
int count = 0;
int i = left, j = mid + 1;//ij分别为左半部分和右半部分的起点
int index = 0;
while(i <= mid && j <= right) {
if(nums[i] <= nums[j]) {
tmp[index] = nums[i];
index++;
i++;
}
//当nums[i] > nums [j] 构成逆序对
else {
//比如[7,8] [5,6] i=0 j=2
//nums[i]=7 nums[j]=5
//5和7/8都能构成逆序对
//所以j可以和nums[mid] ~ nums[i]之前的数字构成逆序对
count += mid - i + 1;
tmp[index] = nums[j];
index++;
j++;
}
}
//多余部分的处理
while(i <= mid) {
tmp[index]=nums[i];
index++;
i++;
}
while(j <= right) {
tmp[index]=nums[j];
index++;
j++;
}
//同步nums数组
for(int i = left, j = 0; i <= right; i++,j++) nums[i] = tmp[j];
return count;
}
};
時間計算量O(NlogN)
空間計算量O(N):要素ごとに、個別のマージ関数がオンになってバックトラックするため、N * 1スペースを占有します。