前缀和,差分

1.前缀和

题目:有nums=[1, 3 ,7, 5, 2],求[2,4]下标数字和,[0,3]下标数字和...?

一般的,[L,R]区间和,q次询问

暴力方法

每次询问都去遍历一遍数组(代码省略)

时间复杂度O(q*n)

前缀和方法

nums : 1 3 7 5 2

sum : 1 4 11 16 18

sum前缀和数组的求法:

  • sum[i] = sum[i-1] + arr[i] (i>0)

  • sum[i] = nums[0] (i==0)

如果想要求[2,4]下标数字和,即sum[4] - sum[2-1] = 18 - 4 = 14。

总结,[L, R]区间和求法:

  • [L,R]和 = sum[R] - sum[L-1] (L>0)

  • [L,R]和 = sum[R](L==0)


代码:

/**
  * 前缀和主要用于求,arr[L,R] 区间的和
  * 时间复杂度O(n)
  */
 public class PrevSum {
     public static void main(String[] args) {
         new PrevSum().prevSumOne();
     }
 ​
     // 一维的前缀和
     public void prevSumOne() {
         int[] nums = {1, 3, 7, 5, 2};
         int[] sum = new int[nums.length]; // 前缀和数组
         sum[0] = nums[0];
         for (int i = 1; i < nums.length; i++) { // 求前缀和数组,sum[i] == arr[0,i]的区间和
             sum[i] = sum[i - 1] + nums[i];
         }
 ​
 ​
         int res1 = getSum(sum, 2, 4);
         int res2 = getSum(sum, 0, 3);
         int res3 = getSum(sum, 3, 4);
         System.out.println(res1 + " " + res2 + " " + res3);
     }
     private int getSum(int[] sum, int left, int right) {
         if (left > 0) {
             return sum[right] - sum[left - 1];
         } else {
             return sum[right];
         }
     }
 }

2. 一维差分

题目:有nums=[1, 3 ,7, 5, 2],求[2,4]下标区间加5,[1,3]下标区间加2,最终nuns数组的状态? 一般的,进行若干次[L,R]区间加value,求数组最终状态

暴力方法

每次操作都去遍历数组进行加value(代码省略)

时间复杂度O(n*m)

差分方法

nums : 1 3 7 5 2

diff : 1 2 4 -2 -3 (差分数组就是相邻两个数的差)

sumd: 1 3 7 5 2

我们发现对差分数组进行前缀和能还原原数组,前缀和差分互为逆运算的!

但这个题用不到上面的性质,我们要使用差分标记,即进行多次区间操作的时候并不马上进行加value操作,而是进行标记,最终一起进行加减。

例如[1,3]下标区间加2,即进行标记:diff[1] + 2, diff[4] + 2

一般的差分标记:

  • [L,R] + value == diff[L] + value , diff[R + 1] - value (R < nums.length - 1)

  • [L,R] + value == diff[L] + value (R == nums.length - 1,多开一个空间就不同判断了)


代码:

/**
  * 差分 时间复杂度 O(m + n)
  * 主要用于求 arr[L,R] 区间增加或减一个值,最终arr数组的状态
  */
 public class Difference {
     public static void main(String[] args) {
         new Difference().differenceOne();
     }
 ​
     // 一维差分
     public void differenceOne() {
         int[] nums = {1, 3, 7, 5, 2}; // 例如[2, 4]+5, [1, 3]+2, [0, 2]-3
 ​
         // 差分标记数组,初始化为0就行(可以不用初始化为相邻两个数的差,最后加上原数组的数就行)
         int[] diff = new int[nums.length]; 
         add(diff, 2, 4, 5);
         add(diff, 1, 3, 2);
         add(diff, 0, 2, -3);
 ​
         // 通过差分标记数组,还原操作后的数组
         for (int i = 1; i < nums.length; i++) {  // 1.先让差分标记数组做前缀和
             diff[i] = diff[i - 1] + diff[i];
         }
         for (int i = 0; i < nums.length; i++) {  // 2.此时的diff就是最终操作的区间和值,最后加上原数组的数就行
             nums[i] += diff[i];
         }
         System.out.println(Arrays.toString(nums)); // [-2, 2, 11, 12, 7]
     }
     private void add(int[] diff, int left, int right, int value) {
         // 差分标记数组的公式: [L,R]+v == diff[L]+v, diff[R+1]-V
         diff[left] += value;
         if(right + 1 < diff.length) {
             diff[right + 1] -= value;
         }
     }
 }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值