数状数组(入门)

数状数组

学习数组数组的好处:
树状数组相对于线段树的编码来讲,树状数组的编码并不复杂,反而很简单,对于一些区间求和,区间求最大值,维护区间的问题,反而能更简单的解决

树状数组核心代码:

int lowbit(int i)//lowbit()可以寻找树状数组中的前向位置和后向位置
{
   return i&(-i);
  }

例如要更改一个区间当中的和

void update(int i,int k)//i代表位置,k代表更改量
{
//由于更改k只影响i--n的结果所以
  while(i<=n)
  {
   c[i]+=k;
   i+=lowbit(i);
   }
 }

假如要求一段区间的和我

int getsum(int i)//这里先求1---i的和
{
    int res=0;
    while(i>0)
    {
        res+=c[i];
        i-=lowbit(i);
     }
 }

完整求和代码:

#include
using namespace std;
#define maxn 1005
int a[maxn],c[maxn];
int n;//n个节点
int lowbit(int x)
{
    return x&(-x);
}
void update(int i,int k)//每次在i位置上+k,故i上的所有和更新
{
    while(i<=n)
    {
        c[i]+=k;
        i+=lowbit(i);
    }
}
int getsum(int i)//求1--i的和
{
    int res=0;
    while(i>0)
    {

       res+=c[i];
        i-=lowbit(i);

    }
    return res;
}
int main()
{

   memset(c,0,sizeof(c));
    while(~scanf("%d",&n))
    {
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            update(i,a[i]);//从后往前更新
        }
        int m;
        scanf("%d",&m);
        while(m--)
        {

            int i,j;
            scanf("%d %d",&i,&j);
            int sum=getsum(j)-getsum(i-1);
            printf("%d\n",sum);
        }


    }


}

区间维护

如果我们要修改[l,r]区间的值,我们只需要update(l,k),修改l的父辈,然后在update(r+1,-k)把之前的父辈的值修改回来,区间修改就已经完成了

void update(int i,int k)//在i位置,添加k
{
    while(i<=n)
    {
        c[i]+=k;
        //printf("%d\n",i);
        i+=lowbit(i);
    }
}
update(l,k);
update(r+1,-k);

注意:
在否循环里输入每个节点的值,经过update(),每个点的值就会改为我们操作想要的值

单点查询

int getpoint(int i)//单点查询
{
    int res=0;
    while(i>0)
    {
        res+=c[i];
        //printf("%d\n",i);
        i-=lowbit(i);
    }
    return res;
}//该操作即可完成单点查询,单点查询的查询的值主要update()
的操作

区间求取最大值

#include 
#include 
#include 
#include 
#include
using namespace std;

typedef long long ll;
const int Maxn = 2e5+10;

int a[Maxn], h[Maxn], n;  // a[] 数列的值,h[] 区间最大值

void add (int x, int val)
{
    while (x <= n)
    {
        h[x] = max (h[x], val);
        x += x&(-x);
    }
}

int query (int L, int R)
{
    int ret = 0, len = R-L+1;
    while (len && R)    // len 是当前还需要判断的范围长度,R是对应区间最大值的下标
    {
        if (len < (R&(-R)))
        {
            ret = max (ret, a[R]);
            R--;
            len--;
        }
        else
        {
            ret = max (ret, h[R]);
            len -= (R&(-R)); // 不断的缩短要判断的区间长度
            R -= (R&(-R));
        }
    }
    return ret;
}

int main (void)
{
    int m;
    while (scanf ("%d%d", &n, &m) != EOF)
    {
        memset (h, 0, sizeof (h));
        for (int i = 1; i <= n; ++i)
        {
            scanf ("%d", &a[i]);
            add (i, a[i]);
        }
        char op[2], b[10];
        int x, y;
        while (m--)
        {
            scanf ("%s%d%d", op, &x, &y);
            gets (b);
            if (!strcmp (op, "U"))
            {
                a[x] = y;
                add (x, y);
            }
            else
            {
                printf ("%d\n", query (x, y));
            }
        }

    }
    return 0;
}

你可能感兴趣的:(回顾算法,数据结构)