树状数组解析

作者: Tim在路上 | 来源:发表于2020-09-08 21:11 被阅读0次

树状数组所能解决的典型问题就是存在一个长度为n的数组,我们如何高效进行如下操作:

update(idx, delta):将num加到位置idx的数字上。

prefixSum(idx):求从数组第一个位置到第idx(含idx)个位置所有数字的和。

rangeSum(from_idx,to_idx):求从数组第from_idx个位置到第to_idx个位置的所有数字的和

lowbit 操作

意思是获取这个数的展开二进制的最低的2的幂方数

lowbit = x & -x;

树状数组的思路是将数组的前缀和拆分为不同的多个数组,正好利用2的幂次方可以将其拆分为log(n) 的时间复杂度

树状数组的定义

定义第i个位置记录(i-lowbit(i),i)数字和;
i 位置的父节点是 i + lowbit(i)

性质: 第i个节点的位置只能由其祖先节点进行覆盖

使用树状数组求范围和,可以采用前缀和之差来进行计算

public class TreeArray {

    int[] tree;
    int[] arr;

    public TreeArray(int[] nums){
        this.arr = nums;
        tree = new int[nums.length + 1];
    }

    public int lowbit(int x){
        return x & -x;
    }

    public void build_tree(){
        // 这里使用更新操作可以降低操作的复杂度
        for(int i=0;i<arr.length;i++){
           tree[i+1] = arr[i];
        }
        for (int i=1;i<tree.length;i++){
            int j = i + lowbit(i);
            if (j < tree.length){
                tree[j] += tree[i];
            }
        }
    }

    // 将数组中的某位增加值,
    public void update_tree(int idx, int val){
        // 这里主要是因为树状数组,都向后移动一位,给0做查询结束的判断
        idx += 1;
        while (idx < tree.length){
            tree[idx] += val;
            idx = idx + lowbit(idx);
        }
    }

    // 求数组的前缀和
    public int query_sum(int idx){
        idx += 1;
        int res = 0;
        while (idx > 0){
            res += tree[idx];
            idx = idx - lowbit(idx);
        }
        return res;
    }
}

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

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。

示例:

输入:nums = [5,2,6,1]
输出:[2,1,1,0]

class Solution {
    private int[] c;
    private int[] a;

    public List<Integer> countSmaller(int[] nums) {
        List<Integer> resultList = new ArrayList<Integer>();
        // 去重排序
        discretization(nums);
        // 初始化比原数组大5的数组进行填0
        init(nums.length + 5);
        for (int i = nums.length - 1; i >= 0; --i) {
            //
            int id = getId(nums[i]);
            // 查询在这个范围内元素个数,并添加进行结果集
            resultList.add(query(id - 1));
            // 在当前位置+1
            update(id);
        }
        Collections.reverse(resultList);
        return resultList;
    }

    private void init(int length) {
        c = new int[length];
        Arrays.fill(c, 0);
    }

    private int lowBit(int x) {
        return x & (-x);
    }

    private void update(int pos) {
        while (pos < c.length) {
            c[pos] += 1;
            pos += lowBit(pos);
        }
    }

    private int query(int pos) {
        int ret = 0;
        while (pos > 0) {
            ret += c[pos];
            pos -= lowBit(pos);
        }

        return ret;
    }

    // 去重排序
    private void discretization(int[] nums) {
        Set<Integer> set = new HashSet<Integer>();
        for (int num : nums) {
            set.add(num);
        }
        int size = set.size();
        a = new int[size];
        int index = 0;
        for (int num : set) {
            a[index++] = num;
        }
        Arrays.sort(a);
    }

    private int getId(int x) {
        return Arrays.binarySearch(a, x) + 1;
    }
}

相关文章

  • 树状数组解析

    树状数组所能解决的典型问题就是存在一个长度为n的数组,我们如何高效进行如下操作: update(idx, delt...

  • 树状数组图文解析

    树状数组是一个查询和修改复杂度都为log(n)的数据结构。主要用于数组的单点修改、区间求和。 lowbit数组 l...

  • 数据结构(二)

    树状数组 POJ 1990: MooFest关于x坐标建立树状数组。先按照v值升序排序,逐个添加到树状数组里。每次...

  • 三种一维树状数组

    单点修改+区间查询 最基本的树状数组 树状数组入门 模板(洛谷P3374 【模板】树状数组1) 区间修改+单点查询...

  • 树状结构转一维数组

    树状结构转数组方法 声明树状对象

  • 树状数组

    复习一下树状数组 树状数组 一种用于处理单点修改和区间查询的数据结构。树状数组C的定义: C[x] = Sum ...

  • 树状数组模板复习

    树状数组模板复习

  • 树状数组求逆序对原理

    归并排序和树状数组都可以用nlogn的算法做到求出逆序对.但这里着重讲树状数组的原理与求法.树状数组最常用的方面就...

  • 「树状数组」

    题目传送门 poj1990题意: 农夫的N (N∈[1, 20,000])头牛参加了"MooFest"之后有了不...

  • 树状数组

    参见: https://www.cnblogs.com/hsd-/p/6139376.htmlhttps://bl...

网友评论

    本文标题:树状数组解析

    本文链接:https://www.haomeiwen.com/subject/bngjektx.html