LeetCode 335. Count of Smaller Numbers After Self
问题描述:
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.
题解:
我们可以采用类似 MergeSort 的算法来解决这个问题,按照 Example 里给的数据 [5, 2, 6, 1] 来一步步分析:
[5, 2, 6, 1] = [5, 2] + [6, 1]
[5, 2] = [5] + [2] | [6, 1] = [6] + [1]
将 [5], [2] 合并成长度为2的数组:
- 2 < 5, 则新数组[2, *], 所有已经被推入新数组的右数组元素[2]
- 右数组已空,则新数组[2, 5], 比该元素小的右数组元素有[2],故有5(1),代表比5小的元素有1个
将 [6], [1] 合并成长度为2的数组:
1 < 6, 则新数组[1, *], 所有已经被推入新数组的右数组元素[1]
右数组已空,则新数组[1, 6], 比该元素小的右数组元素有[1],故有6(1),代表比6小的元素有1个
将 [2, 5], [1, 6] 合并成长度为4的数组:
1 < 2, 则新数组[1, *, *, *], 所有已经被推入新数组的右数组元素[1]
2 < 6, 则新数组[1, 2, *, *], 比该元素小的右数组元素有[1],故有2(1),代表比2小的元素有1个
5 < 6, 则新数组[1, 2, 5, *], 比该元素小的右数组元素有[1],故有5(1 + 1),代表比5小的元素有2个
左数组已空,则新数组[1, 2, 5, 6]
Ok, 目前我们已经算出输入 [5, 2, 6, 1] 的输出 [2, 1, 1, 0]。总结一下就是,记录 MergeSort 每次合并过程中右数组已被推入新数组的元素(但其实我们只需要维护一个 helpCount 变量来记录个数即可),当左数组中有元素推入,则其所维护的统计量(比它小的元素个数)加上 helpCount。
Code:
class Solution {
public:
vector<int> countSmaller(vector<int> &nums)
{
vector<int> indices(nums.size(), 0);
vector<int> result(nums.size(), 0);
for (int i = 0; i < nums.size(); ++i)
indices[i] = i;
helper(nums, indices, result, 0, nums.size() - 1);
return result;
}
private:
void helper(vector<int> &nums, vector<int> &indices,
vector<int> &result, int low, int high)
{
if (low >= high)
return;
int mid = (low + high) / 2;
helper(nums, indices, result, low, mid);
helper(nums, indices, result, mid + 1, high);
int i = low, j = mid + 1, helpCount = 0, k = 0;
vector<int> tmp_nums(high - low + 1), tmp_indices(indices.begin(), indices.end());
while (i <= mid && j <= high)
{
if (nums[i] <= nums[j])
{
tmp_nums[k] = nums[i];
tmp_indices[low + k] = indices[i];
result[indices[i]] += helpCount;
++i;
}
else
{
tmp_nums[k] = nums[j];
tmp_indices[low + k] = indices[j];
++helpCount;
++j;
}
++k;
}
while (i <= mid)
{
tmp_nums[k] = nums[i];
tmp_indices[low + k] = indices[i];
result[indices[i]] += helpCount;
++i;
++k;
}
while (j <= high)
{
tmp_nums[k] = nums[j];
tmp_indices[low + k] = indices[j];
++helpCount;
++j;
++k;
}
for (i = low, j = 0; i <= high; ++i, ++j)
nums[i] = tmp_nums[j];
indices.assign(tmp_indices.begin(), tmp_indices.end());
}
};
复杂度分析:
将问题分解成求两个小数组(左半数组和右半数组,元素个数为 n/2 )的 Count of Smaller Numbers After Self 问题,求完后再在合并过程中求整数组的 Count of Smaller Numbers After Self 问题,而两个小问题又能各自分解成两个小问题,依此递归直至数组元素个数为1。
故我们能写出表达式:T(n) = 2T(n/2) + O(n)
根据大师定理得:a = 2, b = 2, d = 1 即 d = logab,所以表达式复杂度为 O(nlogn)
Ok,如果对大师定理不了解,我们可以这样分析,将一个元素个数为n的数组每次折半,需要几次能将数组大小折为1呢,答案是 log2n 对吧!每一层数组元素个数为上一层的一半(第1层除外)则每一层我们需要遍历数组几次呢,答案是一次,例如[5],[2],[6],[1],要将它们变为长度为2的两个数组,需要遍历这四个数组各一次,所以加起来就是原数组的长度。
其它解法:
- 维护二叉搜索树,将数组的元素从左到右依次插入树中,如果元素比root小,则root的统计量+1,然后进入左子树,不断递归到叶子节点…
- 使用STL的 upper_bound 和 lower_bound 函数…
- …