相关文章推荐:力扣--分治(归并排序)算法题I:排序数组,交易逆序对的总数
目录
1.计算右侧小于当前元素的个数
https://leetcode.cn/problems/count-of-smaller-numbers-after-self/description/

理解题意

算法原理
本题要求找到在某数之后的,比他本身小的数的个数,结合我们上篇文章所述,本题的思路应该是降序排序,找到在此数之后有多少个比我小的数。

情况一:nums[cur1] <= nums[cur2] -> cur2++
情况二:nums[cur1] > nums[cur2] -> ret += right - cur2 + 1; cur1++;
并且本题的关键点在于,如何找到nums中,当前元素的原始下标是多少?( 因为排序会打乱元素的原始位置,如果不记录原始下标,你就不知道该把算出来的"数量"加到结果数组的哪个位置**)**
首先,我们在代码开头对index数组进行初始化,其次,在mergeSort合并时,将元素和其下标一起放入。
class Solution
{
vector<int> ret;
vector<int> index; // 记录nums中当前元素的原始下标
int tmpNums[500010];
int tmpIndex[500010];
public:
vector<int> countSmaller(vector<int>& nums)
{
int n = nums.size();
ret.resize(n);
index.resize(n);
// 初始化index数组
for(int i = 0; i < n; i++)
{
index[i] = i;
}
mergeSort(nums, 0, n - 1);
return ret;
}
void mergeSort(vector<int>& nums, int left, int right)
{
if(left >= right) return;
// 选择中间划分点
int mid = (left + right) >> 1;
// 左右区间排序
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
// 处理一左一右的情况
int cur1 = left, cur2 = mid + 1, i = 0;
while(cur1 <= mid && cur2 <= right) // 降序
{
if(nums[cur1] <= nums[cur2])
{
tmpNums[i] = nums[cur2];
tmpIndex[i++] = index[cur2++];
}
else
{
ret[index[cur1]] += right - cur2 + 1;
tmpNums[i] = nums[cur1];
tmpIndex[i++] = index[cur1++];
}
}
// 处理没有遍历完的数组
while(cur1 <= mid)
{
tmpNums[i] = nums[cur1];
tmpIndex[i++] = index[cur1++];
}
while(cur2 <= right)
{
tmpNums[i] = nums[cur2];
tmpIndex[i++] = index[cur2++];
}
// 还原
for(int i = left; i <= right; i++)
{
nums[i] = tmpNums[i - left];
index[i] = tmpIndex[i - left];
}
}
};
2.翻转对
https://leetcode.cn/problems/reverse-pairs/description/

理解题意
在数组中,返回满足i < j并且nums[ i ] > 2 * nums[ j ]的[ i , j ] 个数
算法原理
归并 ,以及利用单调性,使用同向双指针。
策略一:降序:计算当前元素之后,有多少元素的二倍比我小
计算翻转对:

class Solution
{
int tmp[50010];
public:
int reversePairs(vector<int>& nums)
{
return mergeSort(nums, 0, nums.size() - 1);
}
int mergeSort(vector<int>& nums, int left, int right)
{
if(left >= right) return 0;
int ret = 0;
int mid = (left + right) >> 1;
// 计算左右两侧的翻转对
ret += mergeSort(nums, left, mid);
ret += mergeSort(nums, mid + 1, right);
// 计算翻转对数量
int cur1 = left, cur2 = mid + 1, i = 0;
while(cur1 <= mid)
{
while(cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) cur2++;
if(cur2 > right)
break;
ret += right - cur2 + 1;
cur1++;
}
// 合并
cur1 = left, cur2 = mid + 1, i = 0;
while(cur1 <= mid && cur2 <= right)
{
tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur2++] : nums[cur1++];
}
while (cur1 <= mid) tmp[i++] = nums[cur1++];
while (cur2 <= right) tmp[i++] = nums[cur2++];
for(int i = left; i <= right; i++)
{
nums[i] = tmp[i - left];
}
return ret;
}
};
策略二:升序:计算当前元素之前,有多少元素的一半比我大
计算翻转对:

class Solution
{
int tmp[50010];
public:
int reversePairs(vector<int>& nums)
{
return mergeSort(nums, 0, nums.size() - 1);
}
int mergeSort(vector<int>& nums, int left, int right)
{
if(left >= right) return 0;
int ret = 0;
int mid = (left + right) >> 1;
// 计算左右两侧的翻转对
ret += mergeSort(nums, left, mid);
ret += mergeSort(nums, mid + 1, right);
// 计算翻转对数量
int cur1 = left, cur2 = mid + 1, i = 0;
while(cur2 <= right)
{
while(cur1 <= mid && nums[cur2] >= nums[cur1] / 2.0) cur1++;
if(cur1 > mid)
break;
ret += mid - cur1 + 1;
cur2++;
}
// 合并
cur1 = left, cur2 = mid + 1, i = 0;
while(cur1 <= mid && cur2 <= right)
{
tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];
}
while (cur1 <= mid) tmp[i++] = nums[cur1++];
while (cur2 <= right) tmp[i++] = nums[cur2++];
for(int i = left; i <= right; i++)
{
nums[i] = tmp[i - left];
}
return ret;
}
};
本章完。