zoukankan      html  css  js  c++  java
  • ACM 树形数组

    树状数组(Binary Indexed Tree(BIT), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值。

    树状数组的解法和程序网上有很多,这里我想思考一下这种算法的灵魂,也就是基于什么样的契机和灵感产生了这种绝妙的想法。这是我感兴趣的方向。

    这种算法,主要用于查询数组中任意两个数之间的所有元素之和,而且这个数组我们会经常修改里面任意的数。

    如果抛弃修改数组这个操作,也就是原数组是不变的,只是做查询的话,我们会很容易想到一种方法。

    设 原数组 a[n],我们可以构造另一个数组c[n],取任意下标i,让 c[i] = a[1] + a[2] + …… + a[i]

    这样如果我们要计算 下标k和j之间的数的和(包括k和j),sum = c[j] - c[k-1]

    因此只要构建了数组c[n],查询操作的时间复杂度都是 o(1) 级别的,非常快

    如果加入了修改操作的话,上边的方法就不太适合了,因为如果 修改了 a[i],对应的 c[i]、c[i+1]、……、c[n],都要因此修改。这修改后的时间复杂度是 o(n) 级别的,虽然查询操作还是 o(1) ,但如果修改操作很多,这种方法显然不适合。

    如果说在这种方法之上改进一下,让修改操作的时间复杂度减少,查询操作时间复杂度增加,树状数组这种算法就做到了这一点,让 修改和查询的时间复杂度都统一为o(log(n)),log(n)的好处是n越大,带来的效率优化就越高

    让我们看下改进后的区别

    原来:

    c[1] = a[1]
    c[2] = a[1] + a[2]
    c[3] = a[1] + a[2] + a[3]
    ...
    c[n] = a[1] + a[2] + a[3] + …… + a[n]

    改进后:

    c[1] = a[1]
    c[2] = a[1] + a[2]
    c[3] = a[3]
    c[4] = a[1] + a[2] + a[3] + a[4]
    c[5] = a[5]
    c[6] = a[5] + a[6]
    c[7] = a[7]
    c[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8]
    ...
    c[n] = a[n – 2^k + 1] + ... + a[n] 【其中k为n二进制末尾0的个数】

    从改进后的规律来看,如果我们修改了a[i],我们就不用将所有c[i]之后的元素都修改了,而是有所选取的修改。这个修改的时间复杂度是o(log(n)),可以从n – 2^k + 1这个下标公式看出来。

    用一张树状图来表示的话会非常直观

    树状数组

    查询操作的话,以计算前n个数的和为例,

    sum(7) = c[7] + c[6] + c[4] = c[0x111] + c[0x110] + c[0x100]

    sum(10) = c[10] + c[8] = c[0x1010] + c[0x1000]

    sum(13) = c[13] + c[12] + c[8] = c[0x1101] + c[0x1100] + c[0x1000]

    看到这里聪明的你应该会发现一些规律,以7的二进制 0x111 为例,从右到左逐渐去掉1,  0x110,0x100 也就是 6 和 4,因此得到 c[7] + c[6] + c[4]

    用这种方法就能求出前n个数的和,然后如果我们要计算 下标k和j之间的数的和(包括k和j),sum = c[j] - c[k-1]

    程序如下:

    #include <stdio.h>
    //#include <windows.h>
    #include <string.h>
    
    #define MAX 1000001
    
    int c[MAX];
    
    // 用此方法可以计算二进制数从右到左数第一个1出现的数
    // 例子:
    // 1————1
    // 10————10
    // 110100————100
    // 10111————1
    // 10000————10000
    int lowbit(int t)
    {
        return t&(-t);
        //return n&(n^(n-1));
    }
    
    // 修改数组中某个数,delta是增量
    void modify(int n, int delta)
    {
        while(n <= MAX)
        {
            int d;
            c[n] += delta;
            n += lowbit(n);
        }
    }
    
    // 求前n个数的和
    int sum(int n)
    {
        int result = 0;
        while(n != 0)
        {
            result += c[n];
            n -= lowbit(n);
        }
        return result;
    }
    
    int main()
    {
        int N = 0, M = 0, i = 1;
        scanf("%d %d", &N, &M);
        memset(c, 0, sizeof(c));
        while(N--)
        {
            int temp;
            scanf("%d", &temp);
            modify(i++, temp);
        }
        while(M--)
        {
            char s[10];
            int I = 0, A = 0, num;
            scanf("%s %d %d", &s, &I, &A);
            if(strcmp(s, "QUERY") == 0)
            {
                num = sum(A) - sum(I-1);
                printf("%d
    ", num);
            }
            else if(strcmp(s, "ADD") == 0)
            {
                modify(I, A);
            }
        }
        //system("pause");
    }

    总结:在无修改操作的情况下,我们用c[n]来表示数组a中前n个数的和sum(n),在有修改操作的情况下,我们还是用数组c中的数来表示sum(n),不同的是,这里的会用到多个数如 c[i]、c[j]、c[k]……,而如何通过n来找到i,j,k……这些相关数和构造出数组c,就是树形数组这种算法的关键所在——将数n分解为2的次方数,如2、4、8、16。

  • 相关阅读:
    深入理解Linux修改hostname
    逆水行舟,不进则退
    TNS-12541: TNS:no listener TNS-12560 TNS-00511: No listener
    Linux Tomcat 6.0安装配置实践总结
    Database 'xxx' cannot be upgraded because it is read-only or has read-only file Make the database or files writeable, and rerun recovery.
    Tomcat启动找不到JRE_HOME的解决方法
    ORACLE触发器判断是否更新了某个字段
    MS SQL错误:SQL Server failed with error code 0xc0000000 to spawn a thread to process a new login or connection. Check the SQL Server error log and the Windows event logs for information about possible related problems
    MS SQL 错误:The operation could not be performed because OLE DB provider "SQLNCLI10" for linked server "test" was unable to begin a distributed transaction.
    ORA-01078: failure in processing system parameters & LRM-00109: could not open parameter file
  • 原文地址:https://www.cnblogs.com/sdlwlxf/p/4490691.html
Copyright © 2011-2022 走看看