LC315(权值线段树+离散化)
作者:互联网
315
给你一个整数数组 \(nums\) ,按要求返回一个新数组 \(counts\) 。数组\(counts\) 有该性质:\(counts[i]\) 的值是 \(nums[i]\) 右侧小于 \(nums[i]\) 的元素的数量。
输入:\(nums = [5,2,6,1]\)
输出:\([2,1,1,0]\)
解释:
\(5\) 的右侧有 \(2\) 个更小的元素 \((2 和 1)\)
\(2\) 的右侧仅有 \(1\)个更小的元素 \((1)\)
\(6\) 的右侧有 \(1\) 个更小的元素 \((1)\)
\(1\) 的右侧有 \(0\) 个更小的元素
/**
* ALGORIHTM_315_CPP
* @author : MWT
* @date : 17:58 2022/5/17
*输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
*/
#define int long long
class Solution {
private:
static const int MAXN = 1e5;
struct Node {
int l, r, val;
} T[MAXN << 2];//四倍空间
//建树操作
void build(int root, int l, int r) {
//初始化每个节点的值
T[root].l = l, T[root].r = r, T[root].val = 0;
if (l == r)return;
int mid = (l + r) >> 1;
//递归建树
build(root << 1, l, mid);
build(root, mid + 1, r);
}
void pushup(int root) {
T[root].val = T[root << 1].val + T[root << 1 | 1].val;
}
//单点更新操作 参数2表示更新哪个数字,参数三表示更新多少
void update(int root, int x, int q) {
//边界条件,找到了就直接在该点权值上更新权值
if (T[root].l == T[root].r) {
T[root].val += q;
return;
}
//递归更新
int mid = (T[root].l + T[root].r) >> 1;
if (x <= mid)update(root << 1, x, q);
else update(root << 1 | 1, x, q);
//因为要维护权值线段树的特性,所以要将底部的权值传递至根部
pushup(root);
}
//查询函数,返回[l,r]内的权值
int query(int root, int l, int r) {
//局部变量保存返回值
int ans = 0;
//边界条件:若此层递归的T[root].l和T[root].r能被[l.r]包含就直接返回该范围的权值
if (T[root].l >= l && T[root].r <= r)return T[root].val;
int mid = (T[root].l + T[root].r) >> 1;
//递归查找
if (l <= mid)ans += query(root << 1, l, r);
if (r > mid)ans += query(root << 1 | 1, l, r);
return ans;
}
//查询第len号的元素是什么
int dequery(int root, int len) {
//边界条件,返回
if (T[root].l == T[root].r)return T[root].l;
//如果左子树的权值>=len(即左边的数的数量比要找的排名大),所以就一定在左子树内,就要递归查找左子树
if (T[root << 1].val >= len)return dequery(root << 1, len);
//如果左子树的权值<len,则说明肯定在右子树内,注意递归的参数
else return dequery(root << 1 | 1, len - T[root << 1].val);
}
//离散向量
vector<int> a;
//结果向量
vector<int> res;
//返回离散前的数字,lower_bound()返回x数字所在的下标
int getid(int x) {
return lower_bound(a.begin(), a.end(), x) - a.begin() + 1;
}
public:
vector<int> countSmaller(vector<int> &nums) {
//原容器的长度
int sz = nums.size();
//将原容器内的数据复制到a容器中
a.assign(nums.begin(), nums.end());
//因为要进行unique(),所以先sort()
sort(a.begin(), a.end());
//unique()的作用是去重(将重复的的元素替换到末尾,返回重复元素的第一个指针),erase()进行清除
a.erase(unique(a.begin(), a.end()), a.end());
//创建权值线段树,因为数据范围是1 <= nums.length <= 1e5 -1e4 <= nums[i] <= 1e4,假设元素不重复,就需要有1e5个坑,所以至少开1e5的空间
build(1, 1, 1e5);
//输入:nums = [5,2,6,1]
//输出:[2,1,1,0]
//解释:
//5 的右侧有 2 个更小的元素 (2 和 1)
//2 的右侧仅有 1 个更小的元素 (1)
//6 的右侧有 1 个更小的元素 (1)
//1 的右侧有 0 个更小的元素
//所以反过来扫描nums数组,先扫描到1,查找权值线段树 query(1,1,1-1);查找的返回值是0,然后进行更新1的权值(update(1,1,1)),然后push_back结果到res容器中,进行下一步扫描、
//下一步扫描到6,query(1,1,6-1),因为已经存在 1 了,1属于[1,5]的范围,且 1 的权值是1,所以返回 1,然后进行更操作6的权值+1,然后将结果放回res中
//下一步扫描到2,query(1,1,2-1),query的范围是[1,1],所以返回 1,然后更新 2 的权值+1,然后将结果放回res中
//最后扫描到5,query(1,1,5-1),query的范围是[1,4],范围中存在两个元素的权值为1(分别是2,1),所以返回 2 ,更新5的权值,然后将结果放回到res中
for (int i = sz - 1; i >= 0; --i) {
int id = getid(nums[i]);
res.push_back(query(1, 1, id - 1));
update(1,id , 1);
}
}
}
时间复杂度不是很好
标签:end,nums,int,root,线段,元素,权值,LC315,右侧 来源: https://www.cnblogs.com/ftwftw/p/_0517.html