mirror of
https://github.com/ShusenTang/LeetCode.git
synced 2024-09-02 14:20:01 +00:00
add 315. Count of Smaller Numbers After Self 🍺
This commit is contained in:
parent
dce058e26b
commit
3c3e17c109
@ -225,6 +225,7 @@ My LeetCode solutions with Chinese explanation. 我的LeetCode中文题解。
|
||||
| 310 |[Minimum Height Trees](https://leetcode.com/problems/minimum-height-trees/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/310.%20Minimum%20Height%20Trees.md)|Medium| |
|
||||
| 312 |[Burst Balloons](https://leetcode.com/problems/burst-balloons/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/312.%20Burst%20Balloons.md)|Hard| |
|
||||
| 313 |[Super Ugly Number](https://leetcode.com/problems/super-ugly-number/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/313.%20Super%20Ugly%20Number.md)|Medium| |
|
||||
| 315 |[Count of Smaller Numbers After Self](https://leetcode.com/problems/count-of-smaller-numbers-after-self/)|[C++](solutions/315.%20Count%20of%20Smaller%20Numbers%20After%20Self.md)|Hard| |
|
||||
| 318 |[Maximum Product of Word Lengths](https://leetcode.com/problems/maximum-product-of-word-lengths/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/318.%20Maximum%20Product%20of%20Word%20Lengths.md)|Medium| |
|
||||
| 319 |[Bulb Switcher](https://leetcode.com/problems/bulb-switcher/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/319.%20Bulb%20Switcher.md)|Medium| |
|
||||
| 322 |[Coin Change](https://leetcode.com/problems/coin-change/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/322.%20Coin%20Change.md)|Medium| |
|
||||
|
271
solutions/315. Count of Smaller Numbers After Self.md
Normal file
271
solutions/315. Count of Smaller Numbers After Self.md
Normal file
@ -0,0 +1,271 @@
|
||||
# [315. Count of Smaller Numbers After Self](https://leetcode.com/problems/count-of-smaller-numbers-after-self/)
|
||||
|
||||
# 思路
|
||||
给定一个数组,计算每个数字右边所有小于这个数字的个数。
|
||||
|
||||
## 思路一、BST
|
||||
|
||||
我们从后往前遍历数组,如果某个数字的右边所有数字都是有序的,那么我们就可以使用二分计算该数字右边所有小于这个数字的个数,但是如何维护有序呢,如果使用插入排序,那每次维护有序数组的时间复杂度为O(n),所以总的复杂度为O(n^2),这和暴力法是一样的。
|
||||
|
||||
除了有序数组之外,二叉搜索树也可以进行二分。而每次向二叉树里插入元素的复杂度平均为O(logn),所以总的时间复杂度平均就为O(nlogn)。另外,每个节点需要存放以这个节点为根的树有多少个节点。
|
||||
|
||||
时间复杂度平均为O(nlogn),空间复杂度为O(n)。注意BST可能会退化成链表,这样时间复杂度就为O(n^2)了。
|
||||
|
||||
## 思路二、归并排序
|
||||
|
||||
如果某个元素`nums[i]`大于其右边的某个元素`nums[j]`(j > i),那么这元素`<i, j>`就构成了一个逆序对,所以我们只需要求出以`nums[i]`为第一个元素的逆序对个数。
|
||||
|
||||
求逆序对最经典的方法就是分治,即归并排序。所以这题我们也可以用归并排序,只需要新增一行代码:在进行`merge`时,记录有多少个以`nums[i]`开头的逆序对。
|
||||
|
||||
时间复杂度为O(nlogn),空间复杂度为O(n)。
|
||||
|
||||
## 思路三、线段树/树状数组
|
||||
|
||||
此题还可以用线段树/树状数组做,我们知道线段树和树状数组可以求前缀和,而这题可以转换成求前缀和。具体转换过程如下:
|
||||
|
||||
1. 我们先遍历一遍数组,确定数组中元素的最小值`MIN`和`MAX`,然后想象有一个大小为`MAX - MIN`的全0数组`arr`,在这个数组上构建线段树/树状数组;
|
||||
2. 然后从后往前遍历数组nums,将`arr[nums[i]]++`,更新线段树/树状数组,这样`arr`在区间`(nums[i], MAX]`的元素和即为`nums[i]`右侧它小的元素个数。
|
||||
|
||||
时间复杂度O(nlogN),空间复杂度O(N),其中`N = MAX - MIN`;
|
||||
|
||||
# C++
|
||||
## 思路一
|
||||
``` C++
|
||||
struct BstNode{
|
||||
int val, node_num; // node_num记录这棵树有多少节点
|
||||
BstNode *left, *right;
|
||||
BstNode(int x): val(x), node_num(1), left(NULL), right(NULL){}
|
||||
};
|
||||
|
||||
class Solution {
|
||||
private:
|
||||
void BST_insert(BstNode *root, BstNode *node){
|
||||
root -> node_num += 1;
|
||||
if(node -> val >= root -> val){ // 插入到右子树
|
||||
if(root -> right) BST_insert(root -> right, node);
|
||||
else root -> right = node;
|
||||
}
|
||||
else{ // 插入到左子树
|
||||
if(root -> left) BST_insert(root -> left, node);
|
||||
else root -> left = node;
|
||||
}
|
||||
}
|
||||
|
||||
int count(BstNode *root, int target){
|
||||
if(!root) return 0;
|
||||
|
||||
if(root -> val < target)
|
||||
return 1 + (root -> left == NULL ? 0 : root -> left -> node_num) \
|
||||
+ count(root -> right, target);
|
||||
|
||||
else return count(root -> left, target);
|
||||
}
|
||||
|
||||
public:
|
||||
vector<int> countSmaller(vector<int>& nums) {
|
||||
vector<int>res(nums.size(), 0);
|
||||
if(nums.empty()) return res;
|
||||
BstNode *root = new BstNode(nums.back());
|
||||
for(int i = nums.size() - 2; i >= 0; i--){
|
||||
res[i] = count(root, nums[i]);
|
||||
BstNode *node = new BstNode(nums[i]);
|
||||
BST_insert(root, node);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 思路二
|
||||
``` C++
|
||||
class Solution {
|
||||
private:
|
||||
vector<int>res;
|
||||
void merge_sort(vector<pair<int, int>>&nums_with_idx, int l, int r){
|
||||
if(l >= r) return;
|
||||
int mid = (l + r) / 2;
|
||||
merge_sort(nums_with_idx, l, mid);
|
||||
merge_sort(nums_with_idx, mid+1, r);
|
||||
merge(nums_with_idx, l, mid, r);
|
||||
}
|
||||
|
||||
void merge(vector<pair<int, int>>&nums_with_idx, int l, int mid, int r){
|
||||
vector<pair<int, int>>merged;
|
||||
int i = l, j = mid + 1;
|
||||
while(i <= mid && j <= r){
|
||||
if(nums_with_idx[i].first <= nums_with_idx[j].first){
|
||||
// 与普通归并排序相比新增的一步, 即记录逆序数:
|
||||
res[nums_with_idx[i].second] += j - mid - 1; // nums[i]大于nums[mid+1,...,j-1]
|
||||
merged.push_back(nums_with_idx[i++]);
|
||||
}
|
||||
else merged.push_back(nums_with_idx[j++]);
|
||||
}
|
||||
while(i <= mid){
|
||||
res[nums_with_idx[i].second] += j - mid - 1;
|
||||
merged.push_back(nums_with_idx[i++]);
|
||||
}
|
||||
//while(j <= r) merged.push_back(nums_with_idx[j++]);
|
||||
for(int k = 0; k < merged.size(); k++) nums_with_idx[k+l] = merged[k];
|
||||
}
|
||||
|
||||
public:
|
||||
vector<int> countSmaller(vector<int>& nums) {
|
||||
vector<pair<int, int>>nums_with_idx;
|
||||
res = vector<int>(nums.size(), 0);
|
||||
for(int i = 0; i < nums.size(); i++)
|
||||
nums_with_idx.push_back({nums[i], i});
|
||||
merge_sort(nums_with_idx, 0, nums.size() - 1);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 思路三、树状数组
|
||||
[来源](https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/solution/c-shu-zhuang-shu-zu-by-mryx/)
|
||||
``` C++
|
||||
class Solution {
|
||||
public:
|
||||
int* tree, n;
|
||||
int lowbit(int x){
|
||||
return x&(-x);
|
||||
}
|
||||
void update(int pos, int delta){
|
||||
while (pos <= n){
|
||||
tree[pos] += delta;
|
||||
pos += lowbit(pos);
|
||||
}
|
||||
}
|
||||
int getSum(int pos){
|
||||
int ret = 0;
|
||||
while (pos){
|
||||
ret += tree[pos];
|
||||
pos -= lowbit(pos);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
vector<int> countSmaller(vector<int>& nums) {
|
||||
n = nums.size();
|
||||
vector<int> ret(n);
|
||||
if (n == 0) return ret;
|
||||
|
||||
int minn = -50000, maxx = 50000;
|
||||
for (int i=0;i<n;++i){
|
||||
maxx = max(maxx, nums[i]);
|
||||
minn = min(minn, nums[i]);
|
||||
}
|
||||
n = maxx - minn + 2;
|
||||
tree = new int[n+1];
|
||||
memset(tree, 0, sizeof(int)*n);
|
||||
for (int i=nums.size()-1;i>=0;--i){
|
||||
ret[i] = getSum(nums[i] - minn);
|
||||
update(nums[i]-minn+1, 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 思路三、线段树
|
||||
[来源](https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/solution/c-xian-duan-shu-jie-fa-by-dufre/)
|
||||
|
||||
``` C++
|
||||
struct SegmentTreeNode{
|
||||
int start;
|
||||
int end;
|
||||
int count;
|
||||
|
||||
SegmentTreeNode* left;
|
||||
SegmentTreeNode* right;
|
||||
|
||||
SegmentTreeNode(int _start, int _end):start(_start),end(_end) {
|
||||
count = 0;
|
||||
left = NULL;
|
||||
right = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
class Solution {
|
||||
public:
|
||||
SegmentTreeNode* build(int start, int end){
|
||||
if (start > end)
|
||||
return NULL;
|
||||
|
||||
SegmentTreeNode* root = new SegmentTreeNode(start, end);
|
||||
|
||||
if (start == end){
|
||||
root->count = 0;
|
||||
}else{
|
||||
int mid = start + (end - start)/2;
|
||||
root->left = build(start, mid);
|
||||
root->right = build(mid+1, end);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
int count(SegmentTreeNode* root, int start, int end){
|
||||
if (root == NULL || start>end)
|
||||
return 0;
|
||||
if (start==root->start && end==root->end){
|
||||
return root->count;
|
||||
}
|
||||
int mid = root->start + (root->end - root->start)/2;
|
||||
int leftcount = 0, rightcount = 0;
|
||||
|
||||
if (start <= mid){
|
||||
if (mid < end)
|
||||
leftcount = count(root->left, start, mid);
|
||||
else
|
||||
leftcount = count(root->left, start, end);
|
||||
}
|
||||
|
||||
if (mid < end){
|
||||
if (start <= mid)
|
||||
rightcount = count(root->right, mid+1, end);
|
||||
else
|
||||
rightcount = count(root->right, start, end);
|
||||
}
|
||||
|
||||
return (leftcount + rightcount);
|
||||
}
|
||||
|
||||
void insert(SegmentTreeNode* root, int index, int val){
|
||||
if (root->start==index && root->end==index){
|
||||
root->count += val;
|
||||
return;
|
||||
}
|
||||
|
||||
int mid = root->start + (root->end - root->start)/2;
|
||||
if (index>=root->start && index<=mid){
|
||||
insert(root->left, index, val);
|
||||
}
|
||||
if (index>mid && index<=root->end){
|
||||
insert(root->right, index, val);
|
||||
}
|
||||
|
||||
root->count = root->left->count + root->right->count;
|
||||
}
|
||||
|
||||
vector<int> countSmaller(vector<int>& nums) {
|
||||
vector<int> res;
|
||||
if (nums.empty())
|
||||
return res;
|
||||
res.resize(nums.size());
|
||||
int start = nums[0];
|
||||
int end = nums[0];
|
||||
|
||||
for (int i=1; i<nums.size(); i++){
|
||||
start = min(start, nums[i]);
|
||||
end = max(end, nums[i]);
|
||||
}
|
||||
|
||||
SegmentTreeNode* root = build(start, end);
|
||||
|
||||
for (int i=nums.size()-1; i>=0; i--){
|
||||
res[i] = count(root, start, nums[i]-1);
|
||||
insert(root, nums[i], 1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
```
|
Loading…
Reference in New Issue
Block a user