👦个人主页zoroxs
小和问题
在一个数组中,每一个数左边比当前小的数累加起来,就是这数组的小和
思路一:暴力
暴力查找这个没啥好说的吧,双层for循环直接搞定,累加就可以了
int smallSum(vector<int>& v)
{
int ans = 0;
for (size_t i = 1; i < v.size(); i++)
{
for (size_t j = 0; j < i; j++)
{
ans += v[j] < v[i] ? v[j] : 0;
}
}
return ans;
}
时间复杂度O(N^2)
思路二:归并排序解法
学过归并排序的朋友,应该都了解归并排序是如何变有序的,归并排序变有序的过程可以利用起来求很多问题
我们要找比当前数小的, 不妨换个思路,我们找它的右边有几个比它大的
我们来分析一下
code
int MergeSortSmallSum(vector<int>& nums)
{
//不改变原数组,拷贝一个新数组,归并排序还要使用一个临时数组,所以直接开辟两个
vector<int> data = nums;
vector<int> tmp;
tmp.resize(data.size());
int rangeN = 1;
int ans = 0;
int n = data.size();
while (rangeN < n)
{
for (int i = 0; i < n; i += 2 * rangeN)
{
int begin1 = i;
int end1 = i + rangeN - 1;
int begin2 = i + rangeN;
int end2 = i + 2 * rangeN - 1;
int k = begin1;
//修正
if (end1 >= n)
{
end1 = n - 1;
begin2 = n;
end2 = n - 1;
}
else if (begin2 >= n)
{
begin2 = n;
end2 = n - 1;
}
else if (end2 >= n)
{
end2 = n - 1;
}
//归并
int j = begin1;
while (begin1 <= end1 && begin2 <= end2)
{
//如果是左组先放,就+=右边剩余个数 乘以当前值
if (data[begin1] < data[begin2])
{
ans += data[begin1] * (end2 - begin2 + 1);
tmp[j++] = data[begin1++];
}
else
{
//此处要注意: 如果相等,就要走右组,因为无法确定右边有多少个比它大
tmp[j++] = data[begin2++];
}
}
while (begin1 <= end1)
{
tmp[j++] = data[begin1++];
}
while (begin2 <= end2)
{
tmp[j++] = data[begin2++];
}
for (; k < j; ++k)
{
data[k] = tmp[k];
}
}
rangeN *= 2;
}
return ans;
}
其实代码也比较简单,归并排序的逻辑, 然后累加了一下,关键是思路,归并的过程中
1个数为1组的时候,就没有小和
2个数为1组的时候,就可能有小和产生, 而产生小和累加之后,这已经变成有序了,下次归并就不用考虑这里
…
所以每次都把组内的小和求出 ,所以时间复杂度是O(N * logN)
对数器测试
这里我就只放测试代码,不放上面两个函数了,需要的可以去上面copy
//对数器测试--
//申请一个随机vector
vector<int>& getRandomVector(int maxsize)
{
int size = rand() % maxsize + 2;
vector<int>* v = new vector<int>;
v->resize(size);
for (int i = 0; i < size; ++i)
{
v->operator[](i) =(rand() % 100 - rand() % 100);
}
return *v;
}
int main(void)
{
srand((unsigned int)time(nullptr));
int maxsize = 100;
int times = 10000;
int i = 0;
for (i = 0; i < times; i++)
{
vector<int>& v = getRandomVector(maxsize);
if (smallSum(v) != MergeSortSmallSum(v))
{
cout << "error" << endl;
break;
}
delete& v;
}
if (i == times)
cout << "Nice" << endl;
return 0;
}
逆序对问题(LeetCode链接)
这个题其实思路和上一题非常相似,逆序么,就是每个数右边有几个比它小的,就组成几对,累加起来就OK
我们来分析一下
code
class Solution {
public:
int MergeSortReversePairs(vector<int>& nums)
{
//归并
vector<int> data = nums;
vector<int> tmp = data;
int rangeN = 1;
int n = data.size();
int ans = 0;
while(rangeN < n)
{
for(int i = 0; i < n; i += 2 * rangeN)
{
int begin1 = i;
int end1 = i + rangeN - 1;
int begin2 = i + rangeN;
int end2 = i + 2 * rangeN - 1;
//区间修正
if(end1 >= n)
{
end1 = n - 1;
begin2 = n;
end2 = n - 1;
}
else if(begin2 >= n)
{
begin2 = n;
end2 = n - 1;
}
else if(end2 >= n)
{
end2 = n - 1;
}
int index = begin1;
int k = begin1;
while(begin1 <= end1 && begin2 <= end2)
{
//如果等于走右组
if(data[begin1] > data[begin2])
{
//加上右边有多少对
ans += (end2 - begin2 + 1);
tmp[index++] = data[begin1++];
}
else
{
tmp[index++] = data[begin2++];
}
}
while(begin1 <= end1)
{
tmp[index++] = data[begin1++];
}
while(begin2 <= end2)
{
tmp[index++] = data[begin2++];
}
for(; k < index; ++k)
{
data[k] = tmp[k];
}
}
rangeN *= 2;
}
return ans;
}
int reversePairs(vector<int>& nums) {
return MergeSortReversePairs(nums);
}
};
这道题就过了,不用写对数器了,直接在LeetCode上跑就OK