题目描述
You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].
Example:
Input: [5,2,6,1]
Output: [2,1,1,0]
Explanation:
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.
解题思路
1. 思路一
看完题目第一个想法也是最朴素的想法便是重循环,对于每一个数,遍历其后的所有数,统计小于其数字的个数,很容易看出,这种算法的效率为
。
代码:
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
vector<int> res;
for (int i = 0; i < nums.size(); i++) {
int count = 0;
for (int j = i + 1; j < nums.size(); j++) {
if (nums[i] > nums[j]) {
count++;
}
}
res.push_back(count);
}
return res;
}
};
运行结果:
可见,
的算法效率不是很高。
2. 思路二
经过一定的观察与思考我们可以发现,统计这个某个数字后面比这个数字小的数字个数,不就是统计包含这个数字的逆序对个数吗,而我们可以利用归并排序的过程来求一个数组的逆序对个数,其算法效率为 。
分治思路:将原数组分为左右两个数组,则左边数组中某个元素的逆序对数 = 其在左数组中的逆序对数 + 右数组中比它小的元素个数。
具体来说,就是每次将数组从中间平分成两个数组,分别对左右数组进行排序,排序完毕之后,进行归并操作(即首先用两个指针分别指向左右两个数组的第一个元素,比较两个元素的大小,将小的那个元素加入有序数组,并将相应的指针后移一位)。在归并操作过程中,每当将右边数组的元素加入有序队列的时候,说明该元素比当前左边数组剩余的所有元素都要小,则左边所有元素的统计结果均加一。
代码:
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
//统计计算结果的向量
vector<int> res(nums.size(), 0);
//用于归并排序的向量,其中pair的第一个元素表示值,第二个元素表示该元素在初始数组中的坐标
vector<pair<int, int> > vec;
for (int i = 0; i < nums.size(); i++) {
vec.push_back(make_pair(nums[i], i));
}
//使用归并排序的方法,计算结果
subCounter(vec, res);
return res;
}
private:
void subCounter(vector<pair<int, int> >& nums, vector<int>& res) {
//递归终止条件
if (nums.size() <= 1) return;
//将数组平分为左右两个数组
int mid = nums.size() / 2;
vector<pair<int, int> > left(nums.begin(), nums.begin() + mid);
vector<pair<int, int> > right(nums.begin() + mid, nums.end());
//分别对左右数组进行排序和计算逆序对个数
subCounter(left, res);
subCounter(right, res);
//归并操作
vector<pair<int, int> > conquer;
auto liter = left.begin(), riter = right.begin();
int count = 0;
while (liter != left.end() && riter != right.end()) {
//左数组的元素小,将它加入有序队列,并累加相应的逆序对数
if (liter->first <= riter->first) {
conquer.push_back(*liter);
res[liter->second] += count;
liter++;
//右数组的元素小,将它加入有序队列,逆序对数+1
} else {
conquer.push_back(*riter);
count++;
riter++;
}
}
//将左右数组中剩余的元素加入有序队列
while(liter != left.end()) {
conquer.push_back(*liter);
res[liter->second] += count;
liter++;
}
while(riter != right.end()) {
conquer.push_back(*riter);
riter++;
}
//将排序后的结果赋值给原无序数组
nums.assign(conquer.begin(), conquer.end());
}
};
运行结果:
通过分治的策略,我们将算法效率提升到了
,而这种算法效率大大缩短了运行时间。