樹狀數組(Binary Indexed Tree(B.I.T), Fenwick Tree)是一個查詢和修改複雜度都為log(n)的數據結構。主要用於查詢任意兩位之間的所有元素之和,但是每次只能修改一個元素的值;經過簡單修改可以在log(n)的複雜度下進行範圍修改,但是這時只能查詢其中一個元素的值(如果加入多個輔助數組則可以實現區間修改與區間查詢)。
這種數據結構(算法)並沒有C++和Java的庫支持,需要自己手動實現。在Competitive Programming的競賽中被廣泛的使用。樹狀數組和線段樹很像,但能用樹狀數組解決的問題,基本上都能用線段樹解決,而線段樹能解決的樹狀數組不一定能解決。相比較而言,樹狀數組效率要高很多。
基本介紹
- 中文名:樹狀數組
- 套用範圍:信息學
- 功能:區間和查詢、單點修改
- 時間複雜度:log(n)
樹狀圖概念
int lowbit(int x){return x&(x^(x–1));}
int lowbit(int x){return x&-x;}
充分性
結構體版
#include<iostream>#include<vector>using namespace std;class BinTree:vector<int>{ public: explicit BinTree(int k=0)//默認初始化一個能保存k個元素的空樹狀數組 { assign(k+1,0);//有效下標從1開始,0僅作邏輯用處 } int lowbit(int k) { return k&-k; //也可寫作x&(x^(x–1)) } int sum(int k)//求第1個元素到第n個元素的和 { return k>0?sum(k-lowbit(k))+(*this)[k]:0; } int last()//返回最後一個元素下標 { return size()-1; } void add(int k,int w)//為節點k加上w { if(k>last())return; (*this)[k]+=w; add(k+lowbit(k),w); }};int main(){ BinTree test(123); test.add(27,72); cout<<test.sum(26)<<' '<<test.sum(27)<<' '<<test.sum(123);}
函式版
#include<iostream>using namespace std;int n,m,i,num[100001],t[200001],l,r;//num:原數組;t:樹狀數組 int lowbit(int x){ return x&(-x);}void change(int x,int p)//將第x個數加p { while(x<=n) { t[x]+=p; x+=lowbit(x); } return;}int sum(int k)//前k個數的和 { int ans=0; while(k>0) { ans+=t[k]; k-=lowbit(k); } return ans;}int ask(int l,int r)//求l-r區間和 { return sum(r)-sum(l-1); }int main(){ cin>>n>>m; for(i=1;i<=n;i++) { cin>>num[i]; change(i,num[i]); } for(i=1;i<=m;i++) { cin>>l>>r; cout<<ask(l,r)<<endl; } return 0;}
典題分析
例題1
int Lowbit(int t){return t&(t^(t-1));}FunctionLowbit(t:longint):longint;BeginLowbit:=tand(txor(t-1));End;
intSum(intend){intsum=0;while(end>0){sum+=in[end];end-=Lowbit(end);}returnsum;}FunctionSum(tail:longint):longint;Vars:longint;Begins:=0;whiletail>0dobegininc(s,in[tail]);dec(tail,Lowbit(tail));end;sum:=s;End;
void plus(int pos,int num){while(pos<=n){in[pos]+=num;pos+=Lowbit(pos);}}Procedureplus(p,num:longint);Beginwhilep<=ndobegininc(in[p],num);inc(p,Lowbit(p));end;End.
例題2
programBIT_test;uses sysutils;var a,c:array[1..100000]oflongint; n:longint; ch:char; i,k,t,t1:longint; ti:double;//計時用functionLow_bit(x:longint):longint;//取出x在二進制下最右邊一個1begin exit(x and (-x));end;procedure Modify(pos,x:longint);//改變數據begin while pos<=n do begin inc(c[pos],x); inc(pos,low_bit(pos)) end;end;function sum(pos:longint):longint;//1..pos求和begin sum:=0; while pos>0 do begin inc(sum,c[pos]); dec(pos,low_bit(pos)); end;end;begin readln(n); for i:=1 to n do begin read(a[i]); Modify(i,a[i]); end; readln; ti:=now; readln(k); for i:=1 to k do begin readln(t,t1); writeln(sum(t1)-sum(t-1)); end; writeln((now-ti)*86400*1000:0:0,'MS');end.
線段樹的比較
#include<cstdio>#include<iostream>#include<cstring>#include<cstdlib>int x,n,t;char ques[5];int tree[1500][1500];using namespace std;int lowbit(int a){ return a&-a;}void change(int x,int y){ for(int i=x;i<=n;i+=lowbit(i)) { for(int j=y;j<=n;j+=lowbit(j)) { tree[i][j]++; }}return;}int getsum(int x,int y){ int ans=0; for (int i=x;i>=1;i-=lowbit(i)) { for(int j=y;j>=1;j-=lowbit(j)) { ans+=tree[i][j]; } } return ans;}int main(){ scanf ("%d",&x); for (int i=1;i<=x;i++) { scanf ("%d%d\n",&n,&t); memset(tree,0,sizeof(tree)); for (int i=1;i<=t;i++) { scanf ("%s",&ques); if (ques[0]=='C') { int x1,x2,y1,y2; scanf ("%d%d%d%d",&x1,&y1,&x2,&y2); change(x1,y1); change(x2+1,y1); change(x1,y2+1); change(x2+1,y2+1); } if (ques[0]=='Q') { int x,y; scanf ("%d%d",&x,&y); printf ("%d\n",getsum(x,y)%2); } } printf ("\n"); } return 0;}