首页 > 编程语言 > 详细

2021.03.01 数组子区间和的多次检索

时间:2021-03-01 11:47:49      阅读:27      评论:0      收藏:0      [点我收藏+]

题目描述

303. 区域和检索 - 数组不可变 难度:简单-中等

题解

暴力解法

  题目中给的数组的最大长度为10^4,最多调用方法10^4次,每次调用方法最多全部遍历一次数组,因此暴力解法的复杂度小于10^8,可以求解。

  

 1 class NumArray {
 2     int[] nums;
 3     public NumArray(int[] nums) {
 4         this.nums = nums;
 5     }
 6     
 7     public int sumRange(int i, int j) {
 8         int sum = 0;
 9         for(int k=i;k<=j;k++){
10             sum+=this.nums[k];
11         }
12         return sum;
13     }
14 }
15 
16 /**
17  * Your NumArray object will be instantiated and called as such:
18  * NumArray obj = new NumArray(nums);
19  * int param_1 = obj.sumRange(i,j);
20  */

不成熟的优化解法

  既然是多次查询,可能存在重复的i和j,创建一个HashMap,存储已经找到的部分和,在找部分和时,先看HashMap是否已经存在,如果不存在,才去遍历数组。

 1 class NumArray {
 2     int[] nums;
 3     HashMap<String,Integer> map;
 4     public NumArray(int[] nums) {
 5         this.nums = nums;
 6         this.map = new HashMap();
 7     }
 8     
 9     public int sumRange(int i, int j) {
10         if(map.containsKey(i+"-"+j)){
11             return map.get(i+"-"+j);
12         }
13         int sum = 0;
14         for(int k=i;k<=j;k++){
15             sum+=this.nums[k];
16         }
17         map.put(i+"-"+j,sum);
18         return sum;
19     }
20 }
21 
22 /**
23  * Your NumArray object will be instantiated and called as such:
24  * NumArray obj = new NumArray(nums);
25  * int param_1 = obj.sumRange(i,j);
26  */

模板解法:前缀和

  我们提前维护一个前缀和数组int frontSum[],该数组的第i个元素表示数据数组nums 从0 ~ i-1的和,这样,我们在O(n)的时间里求出了部分和数组,查询时只需求frontSum[j+1] - frontSum[i]即可,查询时间为O(1)。

  

 1 class NumArray {//前缀和
 2     int[] frontSum;
 3     public NumArray(int[] nums) {
 4         this.frontSum = new int[nums.length+1];
 5         for(int i = 1;i < frontSum.length;i++){
 6             frontSum[i] = frontSum[i-1] + nums[i-1];
 7         }
 8     }
 9     
10     public int sumRange(int i, int j) {
11         return this.frontSum[j+1] - this.frontSum[i];
12     }
13 }

引申解法:分块

  既然有前缀和,那分块有啥用呢?事实上,分块是解决很多区间查询问题的通用「大杀器」。如果我们把这题修改一下,除了 sumRange(i, j) 这一查询操作之外,我们增加一个 update(i, x) 操作,将数组元素 nums[i] 的值修改为 xx,那么使用前缀和处理修改操作时,就需要使用 O(n) 的时间,将从 i开始的前缀和全部重新计算一遍。而使用分块方法的话,只需要找到位置 i 对应的块 block_size,使用 O(1)的时间修改这个块的部分和即可。

  

 1 class NumArray {
 2     int blockSize = 100;  // blockSize取法:blockSize * bockSize >= nums.length
 3     int[] nums;
 4     int[] block;          // block[i] 表示[i, i + blockSize - 1]之间的区间和; 可以定义成 ArrayList
 5     
 6     public NumArray(int[] nums) {
 7         this.nums = nums;
 8         block = new int[nums.length / blockSize + 1]; 
 9         int idx = 0;
10         for (int i = 0; i + blockSize <= nums.length; i += blockSize) {
11             int curSum = 0;
12             for (int j = 0; j < blockSize; j++)  // 当前块的元素和
13                 curSum += nums[i + j];
14             block[idx++] = curSum;
15         }
16     }
17     
18     public int sumRange(int i, int j) {
19         int k = i, res = 0;
20         while (k <= j) {
21             // k 右边有个完整块
22             if (k % blockSize == 0 && k + blockSize - 1 <= j) {
23                 res += block[k / blockSize];
24                 k += blockSize;
25             // k 右边不够一个块
26             } else {
27                 res += nums[k];
28                 k++;
29             }
30         }
31         return res;
32     }
33     
34     // 把 i 位置的元素改成 data
35     public void update(int i, int data) {
36         int blockIdx = i / blockSize;
37         block[blockIdx] -= nums[i];
38         block[blockIdx] += data;
39         
40         nums[i] = data;
41     }
42 }

 

2021.03.01 数组子区间和的多次检索

原文:https://www.cnblogs.com/WhiteThornZzf/p/14462127.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!