具体证明也很简单,把线段树看成一个完全二叉树(空结点也当作使用)对于任意一个结点k来说,它所在此二叉树的log2(k) 层,则此层共有2log2(k)个结点,同样对于k的左子树那层来说有2log2(k)+1个结点,则结点k和左子树间隔了2*2log2(k)-k + 2*(k-2log2(k))个结点,然后这就很简单就得到k+2*2log2(k)-k + 2*(k-2log2(k)) = 2*k的关系了吧,右子树也就等于左子树结点+1。
是不是觉得其实很简单,而且因为左子树都是偶数,所以我们常用位运算来寻找左右子树
k<<1(结点k的左子树下标)
k<<1|1(结点k的右子树下标)
整理一下思绪,现在已经明白了数组如何存在线段树,结点间的关系,以及使用递归的方式建立线段树,那么具体如何建立线段树,我们来看代码,代码中不清楚的地方都有详细的注释说明。
1 const int maxn = 100005; 2 int a[maxn],t[maxn<<2]; //a为原来区间,t为线段树 3 4 void Pushup(int k){ //更新函数,这里是实现最大值 ,同理可以变成,最小值,区间和等 5 t[k] = max(t[k<<1],t[k<<1|1]); 6 } 7 8 //递归方式建树 build(1,1,n); 9 void build(int k,int l,int r){ //k为当前需要建立的结点,l为当前需要建立区间的左端点,r则为右端点 10 if(l == r) //左端点等于右端点,即为叶子节点,直接赋值即可 11 t[k] = a[l]; 12 else{ 13 int m = l + ((r-l)>>1); //m则为中间点,左儿子的结点区间为[l,m],右儿子的结点区间为[m+1,r] 14 build(k<<1,l,m); //递归构造左儿子结点 15 build(k<<1|1,m+1,r); //递归构造右儿子结点 16 Pushup(k); //更新父节点 17 } 18 }