From 24e72652f8052e0a87b04c0fa95bf974f9d57857 Mon Sep 17 00:00:00 2001 From: ShusenTang Date: Wed, 12 Aug 2020 19:43:48 +0800 Subject: [PATCH] Update 315. Count of Smaller Numbers After Self.md --- ...15. Count of Smaller Numbers After Self.md | 177 +++++++----------- 1 file changed, 70 insertions(+), 107 deletions(-) diff --git a/solutions/315. Count of Smaller Numbers After Self.md b/solutions/315. Count of Smaller Numbers After Self.md index 31f2f58..6adb1dd 100644 --- a/solutions/315. Count of Smaller Numbers After Self.md +++ b/solutions/315. Count of Smaller Numbers After Self.md @@ -23,11 +23,14 @@ 此题还可以用线段树/树状数组做,我们知道线段树和树状数组可以求前缀和,而这题可以转换成求前缀和。具体转换过程如下: -1. 我们先遍历一遍数组,确定数组中元素的最小值`MIN`和`MAX`,然后想象有一个大小为`MAX - MIN`的全0数组`arr`,在这个数组上构建线段树/树状数组; -2. 然后从后往前遍历数组nums,将`arr[nums[i]]++`,更新线段树/树状数组,这样`arr`在区间`(nums[i], MAX]`的元素和即为`nums[i]`右侧它小的元素个数。 +1. 我们先遍历一遍数组,确定数组中元素的最小值`MIN`和`MAX`,然后想象有一个大小为`MAX - MIN`的全0数组`arr`,`arr[i]=j`表示`i+MIN`出现了j次。然后在这个数组上构建线段树/树状数组; +2. 然后从后往前遍历数组nums,将`arr[nums[i]-MIN]++`,表示出现次数加1,更新线段树/树状数组,这样就可很方便求得右侧比它小的元素个数。 时间复杂度O(nlogN),空间复杂度O(N),其中`N = MAX - MIN`; +关于线段树和树状数组可参考我的博客[Range Sum Query - Mutable (区间查询)](https://tangshusen.me/2019/11/17/range-sum-query-mutable/) + + # C++ ## 思路一 ``` C++ @@ -119,6 +122,71 @@ public: } }; ``` +## 思路三、线段树 +``` C++ +class SegTree{ +private: + int n; + vectortree; // 用一个长为2n的数组来表示树 +public: + SegTree(vector& nums) { + /* 对数组nums建线段树, 方便求sum(nums[i,...,j])以及更新nums[i]*/ + n = nums.size(); + tree = vector(n*2, 0); + + for(int i = 0; i < n; i++) // 建树 + update(i, nums[i]); + } + + void update(int i, int diff) { + /*更新操作: 将nums[i]的值加上diff*/ + i += n; // 转换为线段树下标 + while(i > 0){ + tree[i] += diff; + i >>= 1; + } + } + + int sumRange(int i, int j) { + /**求nums[i,...,j]的和*/ + if(i > j) return 0; + + i += n; j += n; // 转换为线段树下标 + int res = 0; + for(; i <= j; i >>= 1, j >>=1){ + if(i & 1) res += tree[i++]; // 是右孩子 + if(!(j & 1)) res += tree[j--]; + } + return res; + } +}; +class Solution { +public: + vector countSmaller(vector& nums) { + int n = nums.size(); + vectorres(n, 0); + if(!n) return res; + + int min_num = nums[0], max_num = nums[0]; + for(int &num: nums){ + min_num = min(min_num, num); + max_num = max(max_num, num); + } + + // nums_for_SegTree[i] = j 表示数字i出现了j次, 初始全0次 + vectornums_for_SegTree(max_num - min_num + 1, 0); + + SegTree st = SegTree(nums_for_SegTree); + + for(int i = n - 1; i >= 0; i--){ + res[i] = st.sumRange(0, nums[i] - min_num - 1); + st.update(nums[i] - min_num, 1); // 出现次数+1, 更新树 + } + + return res; + } +}; +``` ## 思路三、树状数组 [来源](https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/solution/c-shu-zhuang-shu-zu-by-mryx/) @@ -164,108 +232,3 @@ public: } }; ``` - -## 思路三、线段树 -[来源](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 countSmaller(vector& nums) { - vector res; - if (nums.empty()) - return res; - res.resize(nums.size()); - int start = nums[0]; - int end = nums[0]; - - for (int i=1; i=0; i--){ - res[i] = count(root, start, nums[i]-1); - insert(root, nums[i], 1); - } - - return res; - } -}; -```