标签: 前缀和

LeetCode刷题【238】除自身以外数组的乘积

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内。

不要使用除法,且在 O(n) 时间复杂度内完成此题。

 

示例 1:

输入: nums = [1,2,3,4]
输出: [24,12,8,6]

示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

 

提示:

  • 2 <= nums.length <= 105
  • -30 <= nums[i] <= 30
  • 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内

 

进阶:你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

Related Topics
  • 数组
  • 前缀和

  • 👍 1190
  • 👎 0
  • 前缀和(积)

    class Solution {
        public int[] productExceptSelf(int[] nums) {
            int len = nums.length;
            int[] arrL = new int[len];
            int[] arrR = new int[len];
            arrL[0] = 1;
            arrR[len-1] = 1;
            for (int i = 1; i < nums.length; i++) {
                arrL[i] = nums[i-1] * arrL[i-1];
                int rIdx = len-i-1;
                arrR[rIdx] = nums[rIdx+1] * arrR[rIdx+1];
            }
    
            int[] res = new int[len];
            for (int i = 0; i < res.length; i++) {
                res[i] = arrL[i] * arrR[i];
            }
            return res;
        }
    }

    缩减下

    class Solution {
        public int[] productExceptSelf(int[] nums) {
            int[] res = new int[nums.length];
            res[0] = 1;
            for (int i = 1; i < nums.length; i++) {
                res[i] = res[i-1] * nums[i-1];
            }
            int preR =  nums[nums.length-1];
            for (int r = nums.length-2; r >= 0; r--) {
                res[r] *= preR;
                preR *= nums[r];
            }
            return res;
        }
    }

    LeetCode刷题【1310】子数组异或查询

    有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]

    对于每个查询 i,请你计算从 Li 到 Ri 的 XOR 值(即 arr[Li] xor arr[Li+1] xor ... xor arr[Ri])作为本次查询的结果。

    并返回一个包含给定查询 queries 所有结果的数组。

     

    示例 1:

    输入:arr = [1,3,4,8], queries = [[0,1],[1,2],[0,3],[3,3]]
    输出:[2,7,14,8] 
    解释:
    数组中元素的二进制表示形式是:
    1 = 0001 
    3 = 0011 
    4 = 0100 
    8 = 1000 
    查询的 XOR 值为:
    [0,1] = 1 xor 3 = 2 
    [1,2] = 3 xor 4 = 7 
    [0,3] = 1 xor 3 xor 4 xor 8 = 14 
    [3,3] = 8
    

    示例 2:

    输入:arr = [4,8,2,10], queries = [[2,3],[1,3],[0,0],[0,3]]
    输出:[8,0,4,4]
    

     

    提示:

    • 1 <= arr.length <= 3 * 10^4
    • 1 <= arr[i] <= 10^9
    • 1 <= queries.length <= 3 * 10^4
    • queries[i].length == 2
    • 0 <= queries[i][0] <= queries[i][1] < arr.length
    Related Topics
    • 位运算
    • 数组
    • 前缀和

  • 👍 146
  • 👎 0
  • 树状数组
    基本是模板套用,不过思路要转变下

    原本的前缀和套用为异或操作,本质没有改变

    class Solution {
        public int[] xorQueries(int[] arr, int[][] queries) {
            FenwickTree fenwickTree = new FenwickTree(arr.length);
            for (int i = 0; i < arr.length; i++) {
                fenwickTree.add(i+1, arr[i]);
            }
            int[] ans = new int[queries.length];
            for (int i = 0; i < queries.length; i++) {
                ans[i] = fenwickTree.query(queries[i][0]) ^ fenwickTree.query(queries[i][1]+1);
            }
            return ans;
        }
    
        class FenwickTree{
            int[] arr;
    
            public FenwickTree(int n){
                arr = new int[n+1];
            }
    
            public int lowBit(int i){
                return i & ( -i );
            }
    
            public void add(int idx, int num){
                while (idx < arr.length){
                    arr[idx] ^= num;
                    idx += lowBit(idx);
                }
            }
    
            public int query(int idx){
                int res = 0;
                while (idx > 0 ){
                    res ^= arr[idx];
                    idx -= lowBit(idx);
                }
                return res;
            }
        }
    }

    LeetCode刷题【560】和为 K 的子数组

    给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 

     

    示例 1:

    输入:nums = [1,1,1], k = 2
    输出:2
    

    示例 2:

    输入:nums = [1,2,3], k = 3
    输出:2
    

     

    提示:

    • 1 <= nums.length <= 2 * 104
    • -1000 <= nums[i] <= 1000
    • -107 <= k <= 107
    Related Topics
    • 数组
    • 哈希表
    • 前缀和

  • 👍 1552
  • 👎 0
  • 连续区间和,必然会想到的内容前缀和数组

    所以在这题目中,我们的思路是这样的

    1. 要求区间和为K的子数组的话,那么就需要判断 在当前位置前面,是否存在前缀和为当前区间和减去K的情况存在
    2. 可以在前缀和数组上遍历,当然我们有更好的选择,哈希表
    3. 在求前缀和数组的过程中,用哈希表统计各个值的前缀和出现次数
    4. 那么当前位置的前缀和 与这个位置之前的 前缀和能组成多少个符合 区间和为K的区间就可以等价🐟, 当前位置之前有多少个 当前位置区间和 减去 K的值的区间和的数量
    5. 边界考虑:当什么都没有的时候有一个区间和为0的情况

    代码

    class Solution {
        public int subarraySum(int[] nums, int k) {
            int[] preSum = new int[nums.length];
            HashMap<Integer,Integer> hashMap = new HashMap<>();
            hashMap.put(0,1);
            int idx = -1;
            int count = 0;
    
            while (++idx < nums.length){
                preSum[idx] = nums[idx] + (idx>0?preSum[idx-1]:0);
                count += hashMap.getOrDefault(preSum[idx] - k,0);
                hashMap.put(preSum[idx], hashMap.getOrDefault(preSum[idx],0)+1);
            }
            return count;
        }
    }

    LeetCode刷题【剑指 Offer 66】构建乘积数组

    给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

     

    示例:

    输入: [1,2,3,4,5]
    输出: [120,60,40,30,24]

     

    提示:

    • 所有元素乘积之和不会溢出 32 位整数
    • a.length <= 100000
    Related Topics
  • 数组
  • 前缀和

  • 👍 206
  • 👎 0

  • 前年美团面试遇到过,哎,更新下(前缀 和×,积√),同力扣238题

    当时没做出来,只是灵光一闪想到了可以全乘了然后除以各个数字,后来加了不能用除法的条件后,想了下觉得可能分段预先把个部分分段区间的乘积求起来,但是后来就没后来了。

    最终结果可以直接在int[] a数组上修改,省一点空间?或者其实还可以再极限一点把int[] arrint[] arr2声明成一个数组,然后在运算的key上做文章

    class Solution {
        public int[] constructArr(int[] a) {
            int n = a.length;
            if (n==0){
                return a;
            }
            int[] arr = new int[n];
            int[] arr2 = new int[n];
    
            arr[0] = a[0];
            arr2[n-1] = a[n-1];
            for (int idx = 1; idx < n; idx++) {
                arr[idx] = a[idx]* arr[idx-1];
                int backIdx = n-idx-1;
                arr2[backIdx] = a[backIdx] * arr2[backIdx+1];
            }
            a[0] = arr2[1];
            a[n-1] = arr[n-2];
            for (int i = 1; i < a.length-1; i++) {
                a[i] = arr[i-1] * arr2[i+1];
            }
            return a;
        }
    }

    重新做这题,更新下单数组解法, 同力扣238题,不过238题的测试用例没有空数组的情况
    思路还是一样的,就是先后更新的时候算的问题

    class Solution {
        public int[] constructArr(int[] nums) {
            if (nums.length < 2){
                return nums;
            }
            int[] res = new int[nums.length];
            res[0] = 1;
            for (int i = 1; i < nums.length; i++) {
                res[i] = res[i-1] * nums[i-1];
            }
            int preR =  nums[nums.length-1];
            for (int r = nums.length-2; r >= 0; r--) {
                res[r] *= preR;
                preR *= nums[r];
            }
            return res;
        }
    }

    LeetCode刷题【304】二维区域和检索 – 矩阵不可变

    给定一个二维矩阵 matrix以下类型的多个请求:

    • 计算其子矩形范围内元素的总和,该子矩阵的 左上角(row1, col1)右下角(row2, col2)

    实现 NumMatrix 类:

    • NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化
    • int sumRegion(int row1, int col1, int row2, int col2) 返回 左上角 (row1, col1) 、右下角 (row2, col2) 所描述的子矩阵的元素 总和

     

    示例 1:

    输入: 
    ["NumMatrix","sumRegion","sumRegion","sumRegion"]
    [[[[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]]],[2,1,4,3],[1,1,2,2],[1,2,2,4]]
    输出: 
    [null, 8, 11, 12]
    
    解释:
    NumMatrix numMatrix = new NumMatrix([[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]]]);
    numMatrix.sumRegion(2, 1, 4, 3); // return 8 (红色矩形框的元素总和)
    numMatrix.sumRegion(1, 1, 2, 2); // return 11 (绿色矩形框的元素总和)
    numMatrix.sumRegion(1, 2, 2, 4); // return 12 (蓝色矩形框的元素总和)
    

     

    提示:

    • m == matrix.length
    • n == matrix[i].length
    • 1 <= m, n <= 200
    • -105 <= matrix[i][j] <= 105
    • 0 <= row1 <= row2 < m
    • 0 <= col1 <= col2 < n
    • 最多调用 104 次 sumRegion 方法
    Related Topics
  • 设计
  • 数组
  • 矩阵
  • 前缀和

  • 👍 307
  • 👎 0
  • 和另一个题目LeetCode刷题【1314】矩阵区域和基本一毛一样

    同样的都是矩阵数据,都是求中间某个矩形部分的内的所有数字总和

    求这里的红色区域内的数字总和的话,就是

    红色区域的数字之和 = 蓝色区域的数字之和 – 黄色区域之和 – 紫色区域之和 + 黑色区域之和

    黑色区域的和值之前重复减掉的

    而求各个格子对应的左上区域的前缀和的方法

    29的蓝色框线区域内的所有数字之和 = 23格子代表的紫色框线区域的所有数字之和 + 28代表的黄色框线区域数字之和 – 22代表的黑色框线区域的所有数字之和 + 29这个格子内的值

    class NumMatrix {
    
        int[][] preSum;
    
        public NumMatrix(int[][] matrix) {
            preSum = new int[matrix.length][matrix[0].length];
            for (int row = 0; row < preSum.length; row++) {
                for (int col = 0; col < preSum[row].length; col++) {
                    preSum[row][col] = getMatrixNum( preSum, row-1, col)
                            + getMatrixNum( preSum, row, col-1)
                            - getMatrixNum( preSum, row-1, col-1)
                            + matrix[row][col];
                }
            }
        }
        
        public int sumRegion(int row1, int col1, int row2, int col2) {
            int sum = getMatrixNum(preSum, row2, col2)
                    + getMatrixNum(preSum, row1-1, col1-1)
                    - getMatrixNum(preSum, row2, col1-1)
                    - getMatrixNum(preSum, row1-1, col2);
            return sum;
        }
    
        public int getMatrixNum(int[][] matrix, int row, int col){
            if (row < 0 || col < 0){
                return 0;
            }
            return matrix[row][col];
        }
    }

    LeetCode刷题【1314】矩阵区域和

    给你一个 m x n 的矩阵 mat 和一个整数 k ,请你返回一个矩阵 answer ,其中每个 answer[i][j] 是所有满足下述条件的元素 mat[r][c] 的和: 

    • i - k <= r <= i + k,
    • j - k <= c <= j + k
    • (r, c) 在矩阵内。

     

    示例 1:

    输入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 1
    输出:[[12,21,16],[27,45,33],[24,39,28]]
    

    示例 2:

    输入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 2
    输出:[[45,45,45],[45,45,45],[45,45,45]]
    

     

    提示:

    • m == mat.length
    • n == mat[i].length
    • 1 <= m, n, k <= 100
    • 1 <= mat[i][j] <= 100
    Related Topics
  • 数组
  • 矩阵
  • 前缀和

  • 👍 105
  • 👎 0
  • 理解题意

    根据题意:所求新数组answer[row][col]的值就是原数组的mat[row][col]位置上下左右下标距离k这个这个区域内的所有数字之和

    若距离k为1,则answer[3][3]的值为周围距离k的矩形区域内的所有数字之和。
    那么顺着题意,想要求这片红色区域的所有数字只有的话,我们有两个方法

    遍历这个区域内的所有格子,求和处理
    这片红色区域的数字之和 = 蓝色区域的数字之和 - 黄色区域之和 - 紫色区域之和 + 黑色区域之和
    黑色区域的和值之前重复减掉的那么顺着这个和数组的前缀和非常像的思路,我们要怎么求这某个格子的所有左上格子内的数字之和呢?

    右下角为29的蓝色区域的所有数字之和 = 23格子代表的紫色区域的所有数字之和 + 28代表的黄色区域数字之和 - 22代表的黑色区域的所有数字之和 + 29这个格子内的值

    因为遍历的顺序是从上往下,从左往右,在第29这个格子的时候,前面的22,23,28的格子内的值都是已知的。

    那么思路就很清楚了

    一些边界情况

    1. 在求前缀和的时候,如果是第0行或者是每行的第一个格子,他们的上一行或者前一个格子是不存在的,直接取值0
    2. 当在求第一个图中红色区域的时候,如果红色区域超出了原本的数组的边界,应当取原本数组边界的位置

    代码

    class Solution {
        public int[][] matrixBlockSum(int[][] mat, int k) {
            int[][] preSum = new int[mat.length][mat[0].length];
            for (int row = 0; row < preSum.length; row++) {
                for (int col = 0; col < preSum[row].length; col++) {
                    preSum[row][col] = getMatrixNum( preSum, row-1, col)
                            + getMatrixNum( preSum, row, col-1)
                            - getMatrixNum( preSum, row-1, col-1)
                            + mat[row][col];
                }
            }
    
            int[][] answer = new int[mat.length][mat[0].length];
            for (int row = 0; row < answer.length; row++) {
                for (int col = 0; col < answer[row].length; col++) {
                    answer[row][col] = getPreSumMatrixNum(preSum, row+k, col+k)
                            + getPreSumMatrixNum(preSum, row-k-1, col-k-1)
                            - getPreSumMatrixNum(preSum, row+k, col-k-1)
                            - getPreSumMatrixNum(preSum, row-k-1, col+k);
                }
            }
            return answer;
        }
    
        public int getMatrixNum(int[][] matrix, int row, int col){
            if (row < 0 || col < 0){
                return 0;
            }
            return matrix[row][col];
        }
    
        public int getPreSumMatrixNum(int[][] matrix, int row, int col){
            if (row < 0 || col < 0){
                return 0;
            }
            row = row > matrix.length-1 ? matrix.length-1 : row;
            col = col > matrix[row].length-1 ? matrix[row].length-1 : col;
            return matrix[row][col];
        }
    
    }
    

    本题和另外一题,LeetCode刷题【304】二维区域和检索 – 矩阵不可变基本一样,可以结合起来一起理解

    LeetCode刷题【53】最大子序和

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

     

    示例 1:

    输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
    输出:6
    解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
    

    示例 2:

    输入:nums = [1]
    输出:1
    

    示例 3:

    输入:nums = [0]
    输出:0
    

    示例 4:

    输入:nums = [-1]
    输出:-1
    

    示例 5:

    输入:nums = [-100000]
    输出:-100000
    

     

    提示:

    • 1 <= nums.length <= 3 * 104
    • -105 <= nums[i] <= 105

     

    进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。

    Related Topics
  • 数组
  • 分治
  • 动态规划

  • 👍 3674
  • 👎 0
  • 题解

    常见的求数组区间合的话,那么最先想到的就还是前缀和数组。

    那么遍历这个前缀和数组途中,以第n位结束的前缀和减去n位置之前最小的一个前缀和就代表的是这个以n位置结束的区间合能取到的最大值。

    接下来当移动到n+1位的时候,n+1位的值和只要和之前n位的时候取到的n位之前最小的前缀和相比较,得到较小的一个就是此时之前最小的前缀和了。

    class Solution {
        public int maxSubArray(int[] nums) {
            int i = 1;
            for (; i < nums.length; i++) {
                nums[i] = nums[i] + nums[i-1];
            }
            int max = nums[0];
            int beforMin = 0;
            for (i = 0; i < nums.length; i++) {
                max = Math.max(max,nums[i]-beforMin);
                beforMin = Math.min(nums[i],beforMin);
            }
    
            return max;
        }
    }

    本质还是动态规划没毛病,在n+1位置的时候的判断取值,依赖于之前在n位置的时候取到的值和当前n+1位置取到的值的逻辑判断结果。