给定一个整数数组 nums
,处理以下类型的多个查询:
- 计算索引
left
和right
(包含left
和right
)之间的nums
元素的 和 ,其中left <= right
实现 NumArray
类:
NumArray(int[] nums)
使用数组nums
初始化对象int sumRange(int i, int j)
返回数组nums
中索引left
和right
之间的元素的 总和 ,包含left
和right
两点(也就是nums[left] + nums[left + 1] + ... + nums[right]
)
示例 1:
输入: ["NumArray", "sumRange", "sumRange", "sumRange"] [[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]] 输出: [null, 1, -1, -3] 解释: NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]); numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3) numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1)) numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))
class NumArray {
private int[] nums;
private int[] preSum;
// -2 0 3 -5 2 -1
// 0 -2 -2 1 -4 -2 -3
// preSum 定义为[0, i -1] 所有值的和
public NumArray(int[] nums) {
this.nums = nums;
preSum = new int[nums.length + 1];
for (int i = 1; i < preSum.length; i++) {
preSum[i] = preSum[i - 1] + nums[i - 1];
}
}
public int sumRange(int left, int right) {
return preSum[right + 1] - preSum[left];
}
//[left, right] 之前的和等于 preSum[0, right] - preSum[0, left - 1]
}
/**
* Your NumArray object will be instantiated and called as such:
* NumArray obj = new NumArray(nums);
* int param_1 = obj.sumRange(left,right);
*/
class NumArray {
// preSum[i] 为[0, i - 1]的和 即[0, i)的和
int[] preSum;
public NumArray(int[] nums) {
preSum = new int[nums.length + 1];
for (int i = 0; i < nums.length; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
}
public int sumRange(int left, int right) {
return preSum[right + 1] - preSum[left];
}
}
给定一个二维矩阵 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:
class NumMatrix {
int [][] preSum;
public NumMatrix(int[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
if (m == 0 || n == 0) {
return;
}
preSum = new int[m + 1][n + 1];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
preSum[i + 1][j + 1] = preSum[i][j + 1] + preSum[i + 1][j] - preSum[i][j] + matrix[i][j];
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
return preSum[row2 + 1][col2 + 1] - preSum[row2 + 1][col1]
-preSum[row1][col2 + 1] + preSum[row1][col1];
}
}
给你一个 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]]
class Solution {
private int[][] preSum;
public int[][] matrixBlockSum(int[][] mat, int k) {
int rows = mat.length;
int cols = mat[0].length;
preSum = new int[rows + 1][cols + 1];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
preSum[i + 1][j + 1] = preSum[i][j + 1] + preSum[i + 1][j] - preSum[i][j] + mat[i][j];
}
}
int[][] res = new int[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
int row1 = Math.max(i - k, 0);
int col1 = Math.max(j - k, 0);
int row2 = Math.min(i + k, rows - 1);
int col2 = Math.min(j + k, cols - 1);
res[i][j] = sumRegion(row1, col1, row2, col2);
}
}
return res;
}
public int sumRegion(int row1, int col1, int row2, int col2) {
return preSum[row2 + 1][col2 + 1]
- preSum[row1][col2 + 1]
- preSum[row2 + 1][col1]
+ preSum[row1][col1];
}
}
给你一个整数数组 nums
,请计算数组的 中心下标 。
数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
如果中心下标位于数组最左端,那么左侧数之和视为 0
,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。
如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1
。
// 1 7 3 6 5 6
//0 1 8 11 17 22 28
// 考虑前缀和
class Solution {
public int pivotIndex(int[] nums) {
int n = nums.length;
int[] preSum = new int[n + 1];
for (int i = 0; i < n; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
int sum = preSum[n];
for (int i = 0; i < n; i++) {
if (sum - preSum[i] - nums[i] == preSum[i]) {
return i;
}
}
return -1;
}
}
它的计算方法是从左向右遍历数组,当遍历到数组的 i 位置时,preSum表示 i 位置左边的元素之和。
我们提前计算出所有元素之和 sums_,那么 sums_ - preSum - nums[i] 就是 i 位置右边元素之和。
如果 preSum == sums_ - preSum - nums[i],那么 i 就是满足题目含义的「中心索引」位置。
如果遍历完数组,都没有发现满足题意的「中心索引」,那么返回 -1 。
给你一个整数数组 nums
,返回 数组 answer
,其中 answer[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法,且在 O(n)
时间复杂度内完成此题。
示例 1:
输入: nums =[1,2,3,4]
输出:[24,12,8,6]
class Solution {
public int[] productExceptSelf(int[] nums) {
int length = nums.length;
int[] res = new int[length];
res[0] = 1;
for (int i = 1; i < length; i++) {
res[i] = nums[i - 1] * res[i - 1];
}
int R = 1;
for (int i = length - 1; i >= 0; i--) {
res[i] = res[i] * R;
R = R * nums[i];
}
return res;
}
}
给定一个二进制数组 nums
, 找到含有相同数量的 0
和 1
的最长连续子数组,并返回该子数组的长度。
示例 1:
输入: nums = [0,1] 输出: 2 说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。
示例 2:
输入: nums = [0,1,0] 输出: 2 说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组
class Solution {
// 考虑0为-1,这题就变成了和为0的最长连续子数组
// 0 1
//0 -1 0
public int findMaxLength(int[] nums) {
int n = nums.length;
int[] preSum = new int[n + 1];
for (int i = 0; i < n; i++) {
preSum[i + 1] = preSum[i] + (nums[i] == 0 ? -1 : 1);
}
Map<Integer, Integer> valToIndex = new HashMap<>();
int res = 0;
for (int i = 0; i < preSum.length; i++) {
int val = preSum[i];
if (!valToIndex.containsKey(val)) {
valToIndex.put(val, i);
}else {
res = Math.max(res, i - valToIndex.get(val));
}
}
return res;
}
}
给你一个整数数组 nums
和一个整数 k
,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
- 子数组大小 至少为 2 ,且
- 子数组元素总和为
k
的倍数。
如果存在,返回 true
;否则,返回 false
。
如果存在一个整数 n
,令整数 x
符合 x = n * k
,则称 x
是 k
的一个倍数。0
始终视为 k
的一个倍数。
示例 1:
输入:nums = [23,2,4,6,7], k = 6 输出:true 解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。
// 23 2 4 6 7
//0 23 25 29 35 42
//0 5 1 5 5 0
// sum[j]−sum[i−1]=n∗k
// 同时除以k,这个k要为整数,需要满足 sum[j]和 sum[i−1]对 k 取余相同
class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
int n = nums.length;
int[] preSum = new int[n + 1];
for (int i = 0; i < n; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
Map<Integer, Integer> valToIndex = new HashMap<>();
for (int i = 0; i < preSum.length; i++) {
int val = preSum[i] % k;
if (!valToIndex.containsKey(val)) {
valToIndex.put(val, i);
}
}
for (int i = 1; i < preSum.length; i++) {
int need = preSum[i] % k;
if (valToIndex.containsKey(need)) {
if (i - valToIndex.get(need) >= 2) {
return true;
}
}
}
return false;
}
}
给你一个整数数组 nums
和一个整数 k
,请你统计并返回 该数组中和为 k
的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2 输出:2
示例 2:
输入:nums = [1,2,3], k = 3 输出:2
class Solution {
// 这个需要返回preSum的数量,所以需要维护
// 1 2 3
//0 1 3 6
// -2 0 3
public int subarraySum(int[] nums, int k) {
int len = nums.length;
int[] preSum = new int[len + 1];
for (int i = 0; i < len; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
int count = 0;
for (int left = 0; left < len; left++) {
for (int right = left; right < len; right++) {
if (preSum[right + 1] - preSum[left]==k) {
count++;
}
}
}
return count;
}
}
class Solution {
public int subarraySum(int[] nums, int k) {
int n = nums.length;
Map<Integer, Integer> preSumFreq = new HashMap<>();
preSumFreq.put(0, 1);
int preSum = 0;
int count = 0;
for (int num : nums) {
preSum += num;
if (preSumFreq.containsKey(preSum - k)) {
count += preSumFreq.get(preSum - k);
}
preSumFreq.put(preSum, preSumFreq.getOrDefault(preSum, 0) + 1);
}
return count;
}
}
给定一个二叉树的根节点 root
,和一个整数 targetSum
,求该二叉树里节点值之和等于 targetSum
的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
示例 1:
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8 输出:3 解释:和等于 8 的路径有 3 条,如图所示。
class Solution {
Map<Long, Integer> preSumCount = new HashMap<>();
Long pathSum;
int targetSum;
int res = 0;
public int pathSum(TreeNode root, int targetSum) {
if (root == null) {
return 0;
}
this.pathSum = 0L;
this.targetSum = targetSum;
this.preSumCount.put(0L, 1);
traverse(root);
return res;
}
private void traverse(TreeNode root) {
if (root == null) {
return;
}
pathSum = (long)(pathSum + root.val);
res += preSumCount.getOrDefault(pathSum - targetSum, 0);
preSumCount.put(pathSum, preSumCount.getOrDefault(pathSum, 0) + 1);
traverse(root.left);
traverse(root.right);
preSumCount.put(pathSum, preSumCount.get(pathSum) - 1);
pathSum -= root.val;
}
}
给定一个整数数组 nums
和一个整数 k
,返回其中元素之和可被 k
整除的(连续、非空) 子数组 的数目。
子数组 是数组的 连续 部分。
示例 1:
输入:nums = [4,5,0,-2,-3,1], k = 5 输出:7 解释: 有 7 个子数组满足其元素之和可被 k = 5 整除: [4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]
class Solution {
public int subarraysDivByK(int[] nums, int k) {
int n = nums.length;
int[] perSum = new int[n + 1];
Map<Integer, Integer> remainderToCount = new HashMap<>();
perSum[0] = 0;
remainderToCount.put(0, 1);
int res = 0;
for (int i = 0; i < n; i++) {
perSum[i + 1] = perSum[i] + nums[i];
int curRemainder = perSum[i + 1] % k;
if (curRemainder < 0) {
// 考虑到 preSum[i + 1] 可能是负数,根据 Java 求模的特性,-2 % 3 的结果是 -2
// 但我们实际想要正余数 1,所以这里需要对 curRemainder 进行调整
curRemainder += k;
}
if (remainderToCount.containsKey(curRemainder)) {
int count = remainderToCount.get(curRemainder);
res += count;
remainderToCount.put(curRemainder, count + 1);
}else {
remainderToCount.put(curRemainder, 1);
}
}
return res;
}
}