zoukankan      html  css  js  c++  java
  • 线段树与树状数组的对比应用

    线段树构造

    因为树状数组不需要构造这一过程,所以先讲线段树的构造

    就是用到递归:先设left=1,right=n,然后每一次递归,left、mid和mid+1、right。代码如下:

        void build(int left,int right,int index)
        {
            tree[index].left=left;
            tree[index].right=right;
               if(left==right)
                return ;
            int mid=(right+left)/2;
            build(left,mid,index*2);
            build(mid+1,right,index*2+1);
        }

    ``

    线段树单点修改

    单点修改就是每到一个节点,看这个节点代表着的区间包括不包括这个点,包括就加上。

        void my_plus(int index,int dis,int k)
        {
            tree[index].num+=k;
            if(tree[index].left==tree[index].right)
                return ;
            if(dis<=tree[index*2].right)
                my_plus(index*2,dis,k);
            if(dis>=tree[index*2+1].left)
                my_plus(index*2+1,dis,k);
        }

    树状数组单点修改

    这里有一个很关键的东西,叫做lowbit,lowbit是将一个二进制数的所有高位一都去掉,只留下最低位的1,比如lowbit(5)=lowbit(0101(二进制))=0001(二进制)

    而如果改变x的值,就要加上自己的lowbit,一直加到n,这些节点都要加,比如一共有8个数第3个数要加上k,那么c[0011]+=k;

    c[0011+0001] (c[0100])+=k;

    c[0100+0100] (c[1000])+=k;

    这样就能维护树状数组

        void add(int x,int k)
        {
            while(x<=n)
            {
                tree[x]+=k;
                x+=lowbit(x);
            }
        }

    线段树区间查询

    区间查询就是,每查到一个区间,有三种选择:

    1、如果这个区间被完全包括在目标区间内,那么加上这个区间的和,然后return;

    2、如果这个区间的right>目标区间的left,那么查询这个区间;

    3、如果这个区间的left<目标区间的right,也查询这个区间;

        void search(int index,int l,int r)
        {
            if(tree[index].left>=l && tree[index].right<=r)
            {
                ans+=tree[index].num;
                return ;
            }
            if(tree[index*2].right>=l)
                search(index*2,l,r);
            if(tree[index*2+1].left<=r)
                search(index*2+1,l,r);
        }

    树状数组区间查询

    就是前缀和,比如查询x到y区间的和,那么就将从1到y的和-从1到x的和。

    从1到y的和求法是,将y转为2进制,然后一直减去lowbit(y),一直到0

    比如求1到7的和

    ans+=c[0111];
    ans+=c[0111-0001(0110)];
    ans+=c[0110-0010(0100)];
    ans+=c[0100-0100(c[0]无意义,结束)]
        int sum(int x)
        {
            int ans=0;
            while(x!=0)
            {
                ans+=tree[x];
                x-=lowbit(x);
            }
            return ans;
        }

    线段树区间修改

    和线段树区间查询类似,分为三种

    1、如果当前区间完全属于要加的区间,那么这个区间,也就是节点加上,然后return;

    2、如果这个区间的right>目标区间的left,那么查询这个区间;

    3、如果这个区间的left<目标区间的right,也查询这个区间;

        void pls(int index,int l,int r,int k)
        {
            if(tree[index].left>=l && tree[index].right<=r)
            {
                tree[index].num+=k;
                return ;
            }
            if(tree[index*2].right>=l)
               pls(index*2,l,r,k);
            if(tree[index*2+1].left<=r)
               pls(index*2+1,l,r,k);
        }

    树状数组区间修改

    这就会变的很好玩。如果将x到y区间加上一个k,那就是从x到n都加上一个k,再从y+1到n加上一个-k

    加的移动还是i+=lowbit(i);

        void add(int x,int k)
        {
            while(x<=n)
            {
                tree[x]+=k;
                x+=lowbit(x);
            }
        }

    线段树单点查询

    就是从根节点,一直搜索到目标节点,然后一路上都加上就好了。

        void search(int index,int dis)
        {
            ans+=tree[index].num;
            if(tree[index].left==tree[index].right)
                return ;
            if(dis<=tree[index*2].right)
                search(index*2,dis);
            if(dis>=tree[index*2+1].left)
                search(index*2+1,dis);
        }

    树状数组单点查询

    从x点,一直x-=lowbit(x),沿途都加上就好啦

        int search(int x)
        {
            int ans=0;
            while(x!=0)
            {
                ans+=tree[x];
                x-=lowbit(x);
            }
            return ans;
        }
  • 相关阅读:
    设计模式---了解
    下一阶段目标
    数据结构(七)排序---快速排序
    数据结构(七)排序---归并排序
    数据结构(七)排序---堆排序
    数据结构(七)排序---希尔排序
    数据结构(七)排序---直接插入排序(稳定)
    数据结构(七)排序---简单选择排序
    求助OPC Opc.IDiscovery m_discovery = new OpcCom.ServerEnumerator();
    C#如何:启用和禁用自动绑定重定向 (微软)
  • 原文地址:https://www.cnblogs.com/--lr/p/9345779.html
Copyright © 2011-2022 走看看