数据结构_01_树状数组


树状数组简介

树状数组(Binary Index Tree, BIT)可以以 O ( l o g n ) O(logn) O(logn)的时间复杂度实现单点修改区间查询等功能。

相关LeetCode问题


一、关键要点

1. lowbit操作

用于获取某数的最低位的1,计算公式为:
x & (   x + 1 ) x \& (~x+1) x&( x+1) x & ( − x ) x \& (-x) x&(x)

  • x x x为20,则其二进制表示为10100
  •   x + 1 ~x+1  x+1为01011+1 --> 01100
  • 可见, x x x   x + 1 ~x+1  x+1最低位的1左边每一位都不同(包括符号位),而最低位的1以及其右边每一位都相同。
  • 两者相与,则只有 x x x最低位的1会保留下来

lowbit 操作在树状数组中及其重要

2. 数组中元素的实质

设原数组为 n u m s nums nums,树状数组为 t r e e tree tree,为了方便取值,使两个数组的下标均从1开始。则:

  • i i i的二进制表示为 ∗ ∗ ∗ 100 ***100 100
  • t r e e [ i ] tree[i] tree[i]是nums中第 ∗ ∗ ∗ 100 ***100 100, ∗ ∗ ∗ 001 ***001 001, ∗ ∗ ∗ 010 ***010 010, ∗ ∗ ∗ 011 ***011 011 个元素的和

换一种方式描述

  • 若取出 i i i的最低位1的结果为 a a a
  • t r e e [ i ] tree[i] tree[i]= n u m s [ i ] + n u m s [ i − 1 ] + n u m s [ i − 2 ] + . . . + n u m s [ i − a + 1 ] nums[i]+nums[i-1]+nums[i-2]+...+nums[i-a+1] nums[i]+nums[i1]+nums[i2]+...+nums[ia+1]

3. 树状数组的结构

树状数组示意图

二、Java实现

public class BinaryIndexTree {

    private int[] tree;
    private int[] nums;
    public BinaryIndexTree(int[] nums) {
        this.nums = nums;
        // 数组长度+1,避免采用0下标
        this.tree = new int[nums.length+1];
        for(int i = 0; i<nums.length; i++){
            this.insert(i,nums[i]);
        }
    }

    /**
     * @param index:
     * @param val:
     * @return: void
     * @author: lihen
     * @date: 2022/5/18 15:55
     * @description: 向空数组中加入数据,生成树状数组
     */
    private void insert(int index, int val){
        // nums索引转为tree索引
        index++;
        for(int i = index; i<tree.length; i+=lowBit(i)){
            this.tree[i]+=val;
        }
    }

    /**
     * @param index:
     * @param val:
     * @return: void
     * @author: lihen
     * @date: 2022/5/18 15:55
     * @description: 更新数组中某位置的数
     */
    public void update(int index, int val) {
        try {
            checkIndex(index);
            // nums索引转为tree索引
            index++;
            // 更新树状数组和原数组
            for(int i = index; i<tree.length; i+=lowBit(i)){
                this.tree[i] = this.tree[i] + val - nums[index-1];
            }
            nums[index-1] = val;
        } catch (Exception e) {
            System.out.println("下标越界,修改失败");
        }

    }

    /**
     * @param left:
     * @param right:
     * @return: int
     * @author: lihen
     * @date: 2022/5/18 15:56
     * @description: 获取某个索引位置到另一位置的数之和
     */
    public int sumRange(int left, int right) {
        try {
            checkIndex(left,right);
            if(right<left)return -1;
            // 分别查询0到left-1和0到right的元素和,再进行相减
            return query(right)-query(left-1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     * @param index:
     * @return: int
     * @author: lihen
     * @date: 2022/5/18 15:59
     * @description: 统计原数组中0到index的元素的和
     */
    public int query(int index){
        // nums索引转为tree索引
        index++;
        int res = 0;
        for(int i = index; i>0; i-=lowBit(i)){
            res+=this.tree[i];
        }
        return res;
    }

    /**
     * @param indexes:
     * @return: void
     * @author: lihen
     * @date: 2022/5/18 16:06
     * @description: 检查是否出现越界错误
     */
    private void checkIndex(int ... indexes) throws Exception {
        for (int index : indexes) {
            if(index<0||index>=nums.length)throw new Exception();
        }
    }

    /**
     * @param x:
     * @return: int
     * @author: lihen
     * @date: 2022/5/18 15:07
     * @description: 取最低位的1
     */
    int lowBit(int x){
        return x&(-x);
    }
    
}

总结

树状数组是一种十分优雅的数据结构,自己现在对它的了解还不够深,之后还要多接触相关题目,把树状数组练会练精。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值