ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LC315(权值线段树+离散化)

2022-05-18 00:02:41  阅读:179  来源: 互联网

标签:end nums int root 线段 元素 权值 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有