树状数组和线段树

简介

查询数组区间和复杂度为log(n)
修改数组值复杂度为log(n)

代码

lowbit函数:最低为第一个1的值

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

创建树状数组

int[] nums; //原数组值
int[] tree; //树状数组
public TreeArr(int[] nums) {
    this.nums = new int[nums.length];
   	this.tree = new int[nums.length+1];
   	for(int i = 0; i < nums.length; i++) {
   		update(i, nums[i]);
           this.nums[i] = nums[i];
   	} 
}

更新数组值

int[] nums; //原数组值
int[] tree; //树状数组
public update(int i, int val) {
    int diff = val - nums[i];
    int x = i + 1;
    while(x < tree.length){
    	tree[x] += diff;
    	x += lowbit(x);
    }
}

求区间和

public int sumRange(int n){
    int x = n + 1;
    int sum = 0;
    while(x > 0){
    	sum += tree[x];
    	x -= lowbit(x);
    }
    return sum;
}
public int sumRange(int i, int j) {
    return sumRange(j) - sumRange(i - 1); //i - 1很重要
}

例题练习

  1. 通过指令创建有序数组

线段树

参考文章

https://zhuanlan.zhihu.com/p/106118909

作用

[1,2,3,4,5]建成的线段树
在这里插入图片描述

  • 查找区间和O(lgn)
  • 查找区间最值O(lgn)
  • 更新区间O(lgn)

模版

public static void buildTree(int l, int r, int p, int[] arr, int[] tree) {
    if(l == r) {
        tree[p] = arr[l];
    }
    else {
        int mid = (l + r) / 2;
        buildTree(l, mid, p * 2 + 1, arr, tree); // 先建立左右子节点
        buildTree(mid + 1, r, p * 2 + 2, arr, tree);
        tree[p] = tree[p * 2 + 1] + tree[p * 2 + 2]; // 该节点的值等于左右子节点之和
    }
}
public static void pushDown(int p, int cl, int cr, int[] mark, int[] tree) {
	// 当前索引
    int mid = (cl + cr) / 2;
    // 懒标记下传
    mark[p * 2 + 1] += mark[p];
    mark[p * 2 + 2] += mark[p];
    // 叶子节点的值更新
    tree[p * 2 + 1] += mark[p] * (mid - cl + 1);
    tree[p * 2 + 2] += mark[p] * (cr - mid);
    mark[p] = 0; // 清除标记
}
public static void updateTree(int l, int r, int d, int p, int cl, int cr, int[] mark, int[] tree) {
    // [l, r]:目标区间,需要更新的区间
    // [cl, cr]:当前区间
    if (cl > r || cr < l) { // 区间无交集
        return; // 剪枝
    }
    else if (cl >= l && cr <= r) // 当前节点对应的区间包含在目标区间中
    {
        tree[p] += (cr - cl + 1) * d; // 更新当前区间的值
        if (cr > cl) { // 如果不是叶子节点
            mark[p] += d; // 初始化标记
        }
    }
    else // 与目标区间有交集,但不包含于其中
    {
        int mid = (cl + cr) / 2;
        if(mark[p] != 0) {
            pushDown(p, cl, cr, mark, tree);
        }
        updateTree(l, r, d, p * 2 + 1, cl, mid, mark, tree); // 递归地往下寻找
        updateTree(l, r, d, p * 2 + 2, mid + 1, cr, mark, tree);
        tree[p] = tree[p * 2 + 1] + tree[p * 2 + 2]; // 根据子节点更新当前节点的值
    }
}
public static int queryTree(int l, int r, int p, int cl, int cr, int[] mark, int[] tree) {
    if (cl > r || cr < l)
        return 0;
    else if (cl >= l && cr <= r)
        return tree[p];
    else
    {
        int mid = (cl + cr) / 2;
        pushDown(p, cl, cr, mark, tree);
        return queryTree(l, r, p * 2 + 1, cl, mid, mark, tree) + queryTree(l, r, p * 2 + 2, mid + 1, cr, mark, tree);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值