线段树
来源:互联网 发布:先锋软件学院地址 编辑:程序博客网 时间:2024/06/06 10:03
线段树的作用:(树状数组实现的功能,线段树同样也可以实现)
1、单点更新
2、区间修改
3、区间查询
主要用于高效解决连续空间的动态查询问题。
线段可以用点来表示,存储个人喜欢用数组存储,也可以用链表。
线段储存的信息有最大值,最小值,区间和。
用结构体来存储信息,因为区间更新需要用到延迟标记。
注:递归非常重要…….递归过程都可以用树表示出来
struct Treenode{ int val; int addVal;//延时处理标志};struct Treenode Tree[4*N];int arr[N];
建线段树
//其实递归过程就是建树过程void build(int root,int istart,int iend,int arr[]){ Tree[root].addVal=0; //延迟标记需初始化为0 if(istart==iend){ Tree[root].val=arr[istart]; //递归到叶子节点,需结束继续往下走,往前回溯,更新非叶子节点的值 } else{ int mid=(istart+iend)/2; //一个区间的划分方式,总是从中点划分 build(root*2+1,istart,mid,arr); //深入递归左孩子 build(root*2+2,mid+1,iend,arr); //深入递归右孩子 Tree[root].val=max(Tree[root*2+1].val,Tree[root*2+2].val); //递归到最低点开始回溯更新非叶节点的值 }}
单点更新
void updateone(int root,int istart,int iend,int index,int val){ if(istart==iend){ if(istart==index) Tree[root].val=val; return ; //一定要有这个return,递归到叶子节点的标志 } int mid=(istart+iend)/2; if(index<=mid) //通过比较找到该节点所在的位置,其实线段树就是个二叉树 updateone(root*2+1,istart,mid,index,val); else updateone(root*2+2,mid+1,iend,index,val); Tree[root].val=max(Tree[root*2+1].val,Tree[root*2+2].val); //递归往回走的时候,一路上更新非叶子节点的值}
区间查询
void pushdown(int root){ if(Tree[root].addVal!=0) { //线段信息不同这里的操作不同 //设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递 //所以是 “+=” Tree[root*2+1].addVal+=Tree[root].addVal; Tree[root*2+2].addVal+=Tree[root].addVal; //根据标志域设置孩子节点的值。因为我们是求区间最小值,因此当区间内每个元 //素加上一个值时,区间的最小值也加上这个值 Tree[root*2+1].val+=Tree[root].addVal; Tree[root*2+2].val+=Tree[root].addVal; //传递后,当前节点标记域清空 Tree[root].addVal=0; }}void update(int root,int istart,int iend,int nstart,int nend,int val){ if(istart>nend||iend<nstart) //该区间不在所要更新的区间内 return ; if(istart>=nstart&&iend<=nend) { //找到要更新的区间 Tree[root].addVal+=val; //将这里的延迟标记更新,以后会更新子节点的值 Tree[root].val+=val; //改变线段表示的值 return ; //从这里往回走,不会往下走了,可能这下面还有节点 } pushdown(root); //将标记的值向下传递,该节点以上的值已经被回溯更新过了,所以在查询的地方也有pudhdown函数 int mid=(istart+iend)/2; update(root*2+1,istart,mid,nstart,nend,val); update(root*2+2,mid+1,iend,nstart,nend,val); //根据左右子树的值回溯更新当前节点的值 Tree[root].val=max(Tree[root*2+1].val,Tree[root*2+2].val);}
区间查询
void update(int root,int istart,int iend,int nstart,int nend,int val){ if(istart>nend||iend<nstart) return ; if(istart>=nstart&&iend<=nend) { Tree[root].addVal+=val; Tree[root].val+=val; return ; } pushdown(root); //将标记的值向下传递,该节点以上的值已经被回溯更新过了,所以在查询的地方也有pudhdown函数 int mid=(istart+iend)/2; update(root*2+1,istart,mid,nstart,nend,val); update(root*2+2,mid+1,iend,nstart,nend,val); //根据左右子树的值回溯更新当前节点的值 Tree[root].val=max(Tree[root*2+1].val,Tree[root*2+2].val);}}