认真记录自己的成长,做人事,听天命。
今天认真学习了树状数组这中数据结构,很美的一种结构,下面简单介绍一下树状数组:具体的介绍百度百科很清楚。
我就说一下我对add(update),lowbit,getSum这三个函数的理解
我们知道树状数组是一种求数组连续和的神结构,究竟神在哪里?
1.树状数组求区间和的时间复杂度是logN
2.修改某个只时,同时也修改了区间和,时间复杂度也是logN
那不禁要问了,怎么保证这么高效的呢?
看图
请认真琢磨Cn所管辖的an的个数及下标
C8管辖8个,C7管辖1个,C6管辖2个,C5管辖1个,C4管辖4个,C3管辖1个,C2管辖2个,C1管辖1个....
8=2^3,7=7*(2^0),6=3*(2^1)...可以看出Cn管辖2^exponent个
/*
*字面意思是最低位1----n+lowbit(x)为n在树状数组上的父节点,n-lowbit(x)为n在树状数组上除了Cn所管辖的an之外的an的和
*位运算很神奇....可以在纸上画一画,模拟一下
*/
int lowbit(int x){
return -x&x;
}
/*
*index代表下标,x代表index下标对应值的增量
*/
void add(int index,int x){
while(index<=n){
arr[index]+=x;
index+=lowbit(index);//找到index的父节点
}
}
int getSum(int m){
int ans=0;
while(m>0){
ans+=arr[m];
m-=lowbit(m);//想一想什么意思....
}
return ans;
}
上面讲了基本的原理,但是是针对对树状数组有一点了解的同学,如果感觉有点搞不懂,建议先看一点树状数组的基础。
树状数组的问题一份分为:插线问点和插点问线
problem 116:插点问线
代码:
//插点问线Sum(a,b)=getSum(b)-<span style="font-family: Arial, Helvetica, sans-serif;">getSum(a-1)</span>
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.Scanner;
public class Main {
static int n;
static int[] arr = new int[1000001];
public static void main(String[] args) throws Exception {
Scanner in = new Scanner(new BufferedInputStream(System.in));
PrintStream out = System.out;
n = in.nextInt();
int m = in.nextInt(), first, second;
for (int i = 1; i <= n; i++) {//直接构造树状数组
add(i, in.nextInt());
}
while (m-- != 0) {
char option = in.next().charAt(0);
first = in.nextInt();
second = in.nextInt();
if (option == 'Q') {
out.println(getSum(second) - getSum(first - 1));
} else {
add(first, second);
}
}
out.close();
in.close();
}
static int getSum(int n) {
int ans = 0;
while (n > 0) {
ans += arr[n];
n -= lowbit(n);
}
return ans;
}
static void add(int index, int x) {
while (index <= n) {
arr[index] += x;
index += lowbit(index);
}
}
/**
* 找父节点或孩子
*/
static int lowbit(int x) {
return -x & x;
}
}
problem 522插线问点
代码:
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.Arrays;
public class Main {
static BufferedInputStream bis = new BufferedInputStream(System.in);
static int[] arr = new int[200005];//将负的转为正的,整体平移向右100001
public static void main(String[] args) throws Exception {
PrintStream out = System.out;
int nCase, n, m;
nCase = getInt();
while (nCase-- != 0) {
n = getInt();
m = getInt();
Arrays.fill(arr, 0);
while (n-- != 0) {
add(getInt(), 1);
add(getInt() + 1, -1);//后面的并没有+1,所以减掉
}
while (m-- != 0) {
out.println(getSum(getInt()));
}
}
out.close();
bis.close();
}
static void add(int index, int x) {
index += 100001;
while (index < 200005) {
arr[index] += x;
index += lowbit(index);
}
}
static int getSum(int n) {
n += 100001;
int ans = 0;
while (n > 0) {
ans += arr[n];
n -= lowbit(n);
}
return ans;
}
static int lowbit(int n) {
return -n & n;
}
static int getInt() throws Exception {
int i, temp = 0, mark = 1;
while ((i = bis.read()) < 45)
;
if (i == 45) {
mark = -1;
i = bis.read();
}
while (i > 47) {
temp = temp * 10 + i - 48;
i = bis.read();
}
return temp * mark;
}
}