前言
真心希望各位dalao点赞+收藏~
树状数组
作用:高效求出区间前缀和,允许进行修改操作。 举个栗子: 刚开始有8项,分别为1-8。 首先构建二叉树:
1-8/ |/ |/ |/ |/ |1-4 5-8/ | / |/ | / |1-2 3-4 5-6 7-8/ | / | / | / |1 2 3 4 5 6 7 8
设x为第i个数所在的层数,显然2,4,6,8,3-4,7-8没有任何用处,因为其他t[i](仅需<个)表示树状数组去掉不需要的数组后第i项的值。
void add(int x,int p){while(x<=n){c[x]+=p;//x为下标,c[x]包含x[原来初始的下标x] x+=lowbit(x);//lowbit为转成二进制从后往前第一个为1的值(那一位的权值)}
}
(暴力求解,每次输入一个值都进行如上时间复杂度为O(log n)的操作(只加了当前这个值),时间复杂度O(n log n),空间复杂度O(n))
void build(){for(int i=1;i<=n;i++){t[i]+=a[i];//t[i]肯定包含a[i],而且以前一定没加上,所以要加上t[i+(i&-i)/*相当于lowbit(i)*/]+=t[i];//直接加到上级祖先}
}
(优化求解,直接一次性加给他的祖先,时间复杂度O(n),空间复杂度O(2n))
以上两种建树方法各有优劣,相当于一个时间空间互换的过程。
拓展类型1: 1.求逆序数(对)问题 逆序数是指在第i个数前有多少个>第i个数的数。
树状数组的作用是求出前缀和, 所以我们可以使用类似于桶排序的原理,桶[i]表示i在此时出现的次数。
只需要求第i个数的时候就把桶[第i个数]++就可以了。
PS:一般用离散化使其空间复杂度变小且下标连续。