资讯专栏INFORMATION COLUMN

运用树状数组解决动态数组求和

Barrior / 703人阅读

摘要:对于一组一维数组解决前项和,如果使用的方法需要的时间来找到前项数字的和,但是可以用的时间来更新对应数字的值但是仍然需要的时间来更新牵扯到相应数字数组的和,相反可以使用树状数组来降低运行时间求数组内一段数组的和,但同样我们增加了更新树状数组内

对于一组一维数组解决前n项和,如果使用linear scan的方法, 需要O(n)的时间来找到前n项数字的和,但是可以用O(1)的时间来更新对应数字的值,但是仍然需要Linear的时间来更新牵扯到相应数字数组的和,相反可以使用树状数组来降低运行时间求数组内一段数组的和,但同样我们增加了更新树状数组内任意节点数值的时间。
树状数组(Binary Indexed Tree)中每个节点的值是原数组中一个或几个数组的和,所以在原数组中进行求和操作就是在树状数组中进行节点的求和操作, 相对应的时间复杂度为O(logN)

Binary Index Tree 基于二进制编码建立:

需要保持一个原始数组a[], 和新建立一个树状数组e[],相对应的二进制编码:

1 : 1     a[1]
2 : 10    e[2] = e[1] + a[2];
3 : 011   e[3] = a[3];
4 : 110   e[4] = e[2] + e[3] + a[4];
5 : 101   e[5] = a[5];
6 : 110   e[6] = e[5] + e[6];
7 : 111   e[7] = a[7];
8 : 1000  e[8] = e[4] + e[6] + e[7] + a[8];
e[4] = a[1] + a[2] + a[3] + a[4];

如果二进制表示中末尾有连续的0,则e[i]是原数组中2^k数值的和
因此可以通过计算e[i]的前驱和后继节点来计算出相对应e[i]的值,实现方法:
lowBit = (i & (-i))
因此,我们有一个数组:

int[] nums;
public int[] NumArray(int[] nums) {
    this.nums = nums;
    //Binary Indexed Trees
    int[] tree = new int[nums.length + 1];
    for(int i = 0; i < tree.length; i++) {
        sum = 0;
        lowBit = i & (-i);
        for(int j = i; i > i - lowBit; j--) {
            sum += sum + nums[j - i];
        }
        tree[i] = sum;
}
public void update(int i, int val) {
    //更新差值,从后往前相加
    int diff = val - nums[i];
    nums[i] = val;
    i++;
    for(; i < tree.length; i++) {
        tree[i] += diff;
    }
}
public void sumRange(int i, int j) {
    return sum(i + j) - sum(i);
}
private int sumRange(int i, int j) {
    int sum = 0;
    for(int i = k; i > 0; i -= i & (-i)) {
        sum += tree[i];
    }
    return sum;
}

Binary Index Tree 主要运用是 Range Sum with Mutable Elements:

Range Sum with Mutable Elements 1D
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.
The update(i, val) function modifies nums by updating the element at index i to val.
Example:
Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8

class Solution{
    int[] nums;
    int[] tree;
    int size;
    
    //time: O(nlogn)
    public RangeSumQueryMutable(int[] nums) {
        this.size = nums.length;
        this.tree = new int[size + 1];
        this.nums = new int[size];
    
        for(int i = 0; i < n; i++) {
            updateTree(i, nums[i]);
        }
   }
   
   public void updateTree(int i, int val) {
       i = i + 1;
       while(i < size) {
           tree[i] += val;
           i += i & (-i); //the last set bits, 2s compliment
       }
   }
   
   public void update(int i, int val) {
       if(size == 0) return;
       update(i, val - nums[i]);
       nums[i] = val;
   }
   
   public int sumRange(int i, int j) {
       if(i == 0) return j;
       return getSum(j) - getSum(i - 1);     
   }
   
   private void getSum(int i) {
      int sum = 0;
      i = i + 1;
      while(i > 0) {
          sum += tree[i];
          i -= i & (-i);//go to ancestor
      }
      return sum;
   }


Range Sum with Mutable Elements 2D
Given matrix =

  [[3, 0, 1, 4, 2],
  [5, 6, 3, 2, 1],
  [1, 2, 0, 1, 5],
  [4, 1, 0, 1, 7],
  [1, 0, 3, 0, 5]]

sumRegion(2, 1, 4, 3) -> 8
update(3, 2, 2)
sumRegion(2, 1, 4, 3) -> 10

class Solution {
    int[][] nums;
    int[][] tree;
    int rows;
    int cols;
    public class RangeSum2D(int[][] nums) {
        rows = nums.length;
        cols = nums[0].length;
        nums = new int[rows][cols];
        tree = new int[rows + 1][cols + 1];
        for(int i = 0; i < rows; i++) {
            for(int j = 0; j < cols; j++) {
                update(nums[i][j], i, j);
            }
        }
    }
    public void update(int val, int row, int col) {
        int diff = val - nums[i][j];
        nums[row][col] = val;
        for(int i = row + 1; i < rows; i += (i & (-i))) {
            for(int j = col + 1; i < cols; j += (j & (-j))) {
                tree[i][j] += diff;
            }
        }
    }
   public int sumRegion(int row1, int col1, int row2, int col2) {
        return getSum(row2 + 1, col2 + 1) + getSum(row1, col1) - getSum(row1, col2 + 1) - getSum(row2 + 1, col1);
   }

    private int getSum(int i, int j) {
        int sum = 0;
        for(int i = rows; i > 0; i -= (i & (-i))) {
            for(int j = cols; j > 0; j -= (j & (-j)) {
                sum += tree[i][j];
            }
        }
        return sum;
    }
    //O(m * logn * n * logn)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68340.html

相关文章

  • ❤️两万字《算法 + 数据结构》如何开始❤️

    文章目录 1️⃣前言:追忆我的刷题经历2️⃣算法和数据结构的重要性?1、适用人群?2、有何作用?3、算法简介?4、数据结构 3️⃣如何开始持续的刷题?1、立军令状?‍❤️‍?2、培养兴趣?3、狂切水题??4、养成习惯?5、一周出师 4️⃣简单数据结构的掌握?1、数组?2、字符串?3、链表?4、哈希表?‍?‍?5、队列?‍?‍?‍?6、栈?7、二叉树?8、多叉树?9、森林?10、树状数组?11、...

    BoYang 评论0 收藏0
  • 斐波那契数列求和的js方案以及优化

    摘要:在上做了一道斐波那契数列求和的题目,做完之后做了一些简单的优化和用另一种方法实现。动态规划解决方案斐波那契数列求和除了可以用递归的方法解决,还可以用动态规划的方法解决。 在codewars上做了一道斐波那契数列求和的题目,做完之后做了一些简单的优化和用另一种方法实现。 题目 function fibonacci(n) { if(n==0 || n == 1) r...

    xinhaip 评论0 收藏0
  • 基于HT for Web的3D树的实现

    摘要:布局树根节点获取到所有的孩子节点对象数组获取孩子节点个数计算张角根据三角函数计算绕父亲节点的半径获取父亲节点的位置坐标根据 在HT for Web中2D和3D应用都支持树状结构数据的展示,展现效果各异,2D上的树状结构在展现层级关系明显,但是如果数据量大的话,看起来就没那么直观,找到指定的节点比较困难,而3D上的树状结构在展现上配合HT for Web的弹力布局组件会显得比较直观,一眼...

    liaosilzu2007 评论0 收藏0
  • Linux C语言结构体-学习笔记

    摘要:语言结构体简介前面学习了语言的基本语法特性,本节进行更深入的学习。下节课结构体结构体的声明与定义之前的学习中,使用的变量类型大多是一些比较简单的变量类型。匿名结构体类型。 Linux C语言结构体简介 前面学习了c语言的基本语法特性,本节进行更深入的学习。 预处理程序。 编译指令: 预处理, 宏定义, 建立自己的数据类型:结构体,联合体,动态数据结构 c语言表达式工具 逻辑运算符: ...

    孙淑建 评论0 收藏0
  • 递归就这么简单

    摘要:那么,有了循环,为什么还要用递归呢在某些情况下费波纳切数列,汉诺塔,使用递归会比循环简单很多很多话说多了也无益,让我们来感受一下递归吧。 递归介绍 本来预算此章节是继续写快速排序的,然而编写快速排序往往是递归来写的,并且递归可能不是那么好理解,于是就有了这篇文章。 在上面提到了递归这么一个词,递归在程序语言中简单的理解是:方法自己调用自己 递归其实和循环是非常像的,循环都可以改写成递归...

    dreamtecher 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<