LeetCode 315. 计算右侧小于当前元素的个数

在这里插入图片描述

//解法一:分治 用额外的index数组记录下标 根据index归并排序并计算答案 有点慢我日
class Solution {
    int[] res;
    int[] tmp;
    int[] index;
    public List<Integer> countSmaller(int[] nums) {
        int n = nums.length;
        res = new int[n];
        tmp = new int[n];
        index = new int[n];
        for(int i = 0; i < n; i++)
            index[i] = i;
        merge(nums, 0, n - 1);
        List<Integer> list = new ArrayList<Integer>();
        for(int num : res){
            list.add(num);
        }
        return list;
    }

    public void merge(int[] a, int l, int r){
        if(l == r)
            return;
        int mid = l + r >> 1;
        merge(a, l, mid);
        merge(a, mid + 1, r);
        if(a[index[mid]] <= a[index[mid + 1]])
            return;
        
        //合并
        for(int i = l; i <= r; i++)
            tmp[i] = index[i];
        int i = l;
        int j = mid + 1;
        for(int k = l; k <= r; k++){
            if(i > mid){
                index[k] = tmp[j++];
            }else if(j > r){
                index[k] = tmp[i++];
                res[index[k]] += (r - mid);
            }else{
                if(a[tmp[i]] <= a[tmp[j]]){
                    index[k] = tmp[i++];
                    res[index[k]] += j - mid - 1;
                }else{
                    index[k] = tmp[j++];
                }
            }
        }
    }
}
//解法二:树状数组 慢。。。。
class Solution {
    public List<Integer> countSmaller(int[] nums) {
        LinkedList<Integer> res = new LinkedList<>();
        int len = nums.length;
        if(len == 0) return res;
        //将nums中的元素排序,记录每个元素对应的索引
        TreeSet<Integer> set = new TreeSet();
        for(int i = 0; i < len; i++){
            set.add(nums[i]);
        }
        Map<Integer, Integer> map = new HashMap<>();
        int index = 1;
        for(Integer n : set){
            map.put(n, index);
            index++;
        }
        //利用索引更新并统计
        FenwickTree fenwickTree = new FenwickTree(len + 1);
        for(int i = len - 1; i >= 0; i--){
            index = map.get(nums[i]);
            //在索引位置添加计数1
            fenwickTree.update(index, 1);
            //统计比索引对应元素小的个数
            res.addFirst(fenwickTree.query(index - 1));
        }
        return res;
    }

    //线段树,O(logn)实现单点更新和前缀和计算
    private class FenwickTree {

        private int[] tree;
        private int len;

        public FenwickTree(int n) {
            this.len = n;
            tree = new int[n + 1];
        }
        //更新本节点和父节点
        public void update(int i, int delta) {
            while (i <= this.len) {
                tree[i] += delta;
                i += lowbit(i);
            }
        }
        //求和,找到对应树的节点
        public int query(int i) {
            int sum = 0;
            while (i > 0) {
                sum += tree[i];
                i -= lowbit(i);
            }
            return sum;
        }
        //计算第一个非0的位置,2的幂
        public int lowbit(int x) {
            return x & (-x);
        }
    }
}
//解法三:线段树 也慢。。。
class Solution {
    public List<Integer> countSmaller(int[] nums) {
        LinkedList<Integer> res = new LinkedList<>();
        int len = nums.length;
        if(len == 0) return res;
        //获取区间范围
        int start = nums[0], end = nums[0];
        for(int i = 0; i < len; i++){
            if(nums[i] < start) start = nums[i];
            if(nums[i] > end) end = nums[i];
        }
        //构建树
        SegmentTreeNode root = build(start, end);
        //从右向左,边插入边计数
        for(int i = len - 1; i >= 0; i--){
            //计数小于该元素的区间,所以要减一
            res.addFirst(count(root, start, nums[i] - 1));
            insert(root, nums[i], 1);
        }
        return res;
    }
    //线段树节点,包含左右最值和该区间叶子节点数,子区间不断递减
    private class SegmentTreeNode{
        int start, end, count;
        SegmentTreeNode left, right;

        public SegmentTreeNode(int start, int end){
            this.start = start;
            this.end = end;
            this.count = 0;
            left = null;
            right = null;
        }
    }
    //构建线段树,不断递减区间长度
    private SegmentTreeNode build(int start, int end){
        if(start > end) return null;
        SegmentTreeNode root = new SegmentTreeNode(start, end);
        if(start != end){
            int mid = start + (end - start) / 2;
            root.left = build(start, mid);
            root.right = build(mid + 1, end);
        }
        return root;
    }
    //插入并更新叶子节点
    private 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;
    }
    //根据区间统计
    private int count(SegmentTreeNode root, int start, int end){
        //nums[i] - 1, 排除相等的情况
        if(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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水之积也不厚,则其负大舟也无力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值