线段树

来源:互联网 发布:先锋软件学院地址 编辑:程序博客网 时间: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);}}
原创粉丝点击