LeetCode Problem Description
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:
Given nums = [5, 2, 6, 1]
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.
Return the array [2, 1, 1, 0]
.
Solution
At first, I try to directly compare all the elements n2 times just as the following code shows:
vector<int> Solution::countSmaller (vector<int> & nums) {
vector<int> result (nums.size(), 0);
int current = 0;
for (vector<int>::iterator i = nums.begin(); i != nums.end(); i++) {
for (vector<int>::iterator j = i; j != nums.end(); j++) {
if (*j < *i) {
result[current]++;
}
}
current++;
}
return result;
}
However, it doesn’t statisfy the requirement of the problem. Then I get some idea from this method
which using the merge sort and the cost is
nlog(n)
. I will explain what I got from this solution. Here is the code of the solution:
class Solution {
public:
vector<int> countSmaller (vector<int> & nums);
protected:
void merge_countSmaller(vector<int>& indices, int first, int last,
vector<int>& results, vector<int>& nums) {
int count = last - first;
if (count > 1) {
int step = count / 2;
int mid = first + step;
merge_countSmaller(indices, first, mid, results, nums);
merge_countSmaller(indices, mid, last, results, nums);
vector<int> tmp;
tmp.reserve(count);
int idx1 = first;
int idx2 = mid;
int semicount = 0;
while ((idx1 < mid) || (idx2 < last)) {
if ((idx2 == last) || ((idx1 < mid) && (nums[indices[idx1]] <= nums[indices[idx2]]))) {
tmp.push_back(indices[idx1]);
results[indices[idx1]] += semicount;
++idx1;
} else {
tmp.push_back(indices[idx2]);
++semicount;
++idx2;
}
}
move(tmp.begin(), tmp.end(), indices.begin()+first);
}
}
};
//The improved one using the mergesort algorithm
vector<int> Solution::countSmaller (vector<int> & nums) {
int n = nums.size();
vector<int> results(n, 0);
vector<int> indices(n, 0);
iota(indices.begin(), indices.end(), 0);
merge_countSmaller(indices, 0, n, results, nums);
return results;
}
In my mind, this merge sort algorithm is tricky. And it cost my a lot of time to understand it. The array indices is used to stored the sorted indeices of the previous array. And since we use the merge sort algorithm, we sort the small partition first and then we merge them into a bigger partition and repeat the sort. And we can get that thought the partition is sorted, the elements in that partition still has the same elements. We sort the partitions just to make sure that every element that bigger than the current element have the count of the smaller elements in another partition is at least as much as the current one’s. Keep adding the count with other partitions and at last we will get the result we want.