树状数组初学(1)——位置i左(右)边小于a[i]的个数

/*树状数组案例:
       给出数组a[1], a[2], ... , a[n]
  输出数组ans[1], ans[2], ... , ans[n] 满足
  ans[i](0 < i <= n)的值是集合{a[j] | 0 < j < i 且 a[j] < a[i])}
  所包含的元素的个数*/
#include 
#define N 1000

int a[N+10], n;
int ans[N+10], tree[N+10];
void update(int x, int value);
int getsum(int x);

int main()
{
    int i, j;

    scanf("%d", &n);
    for (i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    /*核心部分*/
    for (i = 1; i <= n; i++) { 
    /*如果是统计位置i右边小于a[i]的个数
      循环改写为for (i = n; i >= 1; i--)即可*/
        ans[i] = getsum(a[i]);
        update(a[i], 1);
    }

    for (i = 1; i <= n; i++)
        printf("%d ", ans[i]);
    printf("\n");

    return 0;
}

void update(int x, int value)
{
    while (x <= N) {
        tree[x] += value;
        x += x & (-x);
    }
    return;
}

int getsum(int x)
{
    int sum = 0;
    while (x > 0) {
        sum += tree[x];
        x -= x & (-x);
    }
    return sum;
}

思路阐述

了解树状数组是什么,怎么实现并不困难,空有代码模版,但是不会应用,这是作为菜鸟的我目前遇到的一大瓶颈。

         先整理一下树状数组的基本功能。这个案例实际上只是对数组a中出现的数的统计,相当于“画正字”。假定有一个桶(值空间)book数组,建立关于数组book的树状数组(在本案例中,没有数组book,因为只要有tree,即可完成一切操作,为了方便思考和理解,需要还原桶book)

刚开始数组book全为0。初始化树状数组tree都为0,从左往右对数组a中出现的数进行统计,出现了a[i]就在a[i]的位置上加1,book[a[i]]  += 1(当然要更新树状数组tree,

Update(a[i], 1)),小于等于a[i]的数1,2,3,. . .,j,. . .,a[i],如果在位置i左边出现过,book[j]等于1,否则等于0,所以ans[i] = book[1] + book[2] +. . . + book[a[i]] = getsum(a[i])

         所以对于从左往右每个位置i的a[i],先求出在其左边小于等于它的数的个数

ans[i],就是getsum(a[i]) =book[1] + book[2] + … +book[a[i]],再对book[a[i]] += 1,也就是update(a[i], 1)

学习体会

         这就是树状数组的一个基本功能,代替桶(值空间),高效地进行求和操作。由于这里的树状数组tree所表示的桶book数组不需要写出来,所以刚开始学习的时候,根本看不懂别人的代码。还原树状数组的“根基”,然后进行理解,这才发现树状数组的奥秘。


你可能感兴趣的:(数据结构笔记)