前言
除了最常规的暴力破解,还要根据题意考虑更优质解答。
题目
描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。
即输出
P
m
o
d
1000000007
P mod 1000000007
Pmod1000000007
数据范围:
对于 50% 的数据,
s
i
z
e
≤
1
0
4
size≤10^4
size≤104
对于 100%100% 的数据,
s
i
z
e
≤
10
size≤10%5
size≤10
数组中所有数字的值满足
0
≤
v
a
l
≤
1
0
9
0≤val≤10^9
0≤val≤109
要求:空间复杂度
O
(
n
)
O(n)
O(n),时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
输入描述:
题目保证输入的数组中没有的相同的数字
示例1
输入:
[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
0
]
[1,2,3,4,5,6,7,0]
[1,2,3,4,5,6,7,0]
返回值:
7
7
7
示例2
输入:
[
1
,
2
,
3
]
[1,2,3]
[1,2,3]
返回值:
0
0
0
解决方案一
1.1 思路阐述
题目最简单的方式就是暴力破解,直接遍历一遍即可。
序列的每一个数和它后面的子序列中的每一个数进行比较,如果遇到小的数,则构成逆序对,否则就继续遍历。
两重循环即可搞定,这样做下来空间复杂度是 O ( 1 ) O(1) O(1)时间复杂度 O ( n 2 ) O(n^2) O(n2)。
这里有一个注意的地方在于count的变量类型,因为最后的数据要对1000000007进行求余后输出,所以如果用int类型,那么最后的数据大小不够,所以要用64位的数据类型。这里用的是long int。
1.2 源码
int InversePairs(vector<int>& nums) {
// write code here
long int count=0;
for (int i=0; i<nums.size(); i++) {
for (int j=i+1; j<nums.size(); j++) {
if(nums[i]>nums[j])
{
count++;
}
}
}
return count%1000000007;
}
解决方案二
2.1 思路阐述
对于解法一,这是最简单的 ,但是并不满足题目要求。
根据题目的要求提示,能看得出来这里最好用的是归并排序的方法。
这里引用一下官方的解答。
因为我们在归并排序过程中会将数组划分成最小为1个元素的子数组,然后依次比较子数组的每个元素的大小,依次取出较小的一个合并成大的子数组。
这里我们也可以用相同的方法划分,划分之后相邻一个元素的子数组就可以根据大小统计逆序对,而不断往上合并的时候,因为已经排好序了,我们逆序对可以往上累计。我们主要有以下三个阶段。
具体做法:
step 1: 划分阶段:将待划分区间从中点划分成两部分,两部分进入递归继续划分,直到子数组长度为1.
step 2: 排序阶段:使用归并排序递归地处理子序列,同时统计逆序对,因为在归并排序中,我们会依次比较相邻两组子数组各个元素的大小,并累计遇到的逆序情况。而对排好序的两组,右边大于左边时,它大于了左边的所有子序列,基于这个性质我们可以不用每次加1来统计,减少运算次数。
step 3: 合并阶段:将排好序的子序列合并,同时累加逆序对。
2.2 源码
class Solution {
public:
int mod = 1000000007;
int mergeSort(int left, int right, vector<int>& data, vector<int>& temp) {
//停止划分
if (left >= right)
return 0;
//取中间
int mid = (left + right) / 2;
//左右划分合并
int res = mergeSort(left, mid, data, temp) + mergeSort(mid + 1, right, data,
temp);
//防止溢出
res %= mod;
int i = left, j = mid + 1;
for (int k = left; k <= right; k++)
temp[k] = data[k];
for (int k = left; k <= right; k++) {
if (i == mid + 1)
data[k] = temp[j++];
else if (j == right + 1 || temp[i] <= temp[j])
data[k] = temp[i++];
//左边比右边大,答案增加
else {
data[k] = temp[j++];
//统计逆序对
res += mid - i + 1;
}
}
return res % mod;
}
int InversePairs(vector<int> data) {
int n = data.size();
vector<int> res(n);
return mergeSort(0, n - 1, data, res);
}
};
总结
最近整理了一下各种排序的算法,暂时还没整理到归并排序,所以这里第一优化的反应是考虑用二分查找的方式,但是二分查找一般涉及到有序的数列。这里还是不符合。
这里我们想要的是一种分治的思想,所以归并排序会更好。