031 连续数组
题目描述:

1.1 解法一:暴力解法
暴力解法就是枚举所有的子数组,然后判断子数组是否满足要求。
1.2 解法二:前缀和在哈希表中
将 0 记为 -1,1 记为 1,问题就变成了找出一段区间,这段区间的和等于 0。于是,这道题就和【和为 K 的子数组】的思路一样了。

设 i 为数组中的任意位置,用 sum[i] 表示 [0, i] 区间中的所有元素的和。
想知道最大的以 i 结尾的和为 0 的子数组,就要找到从左往右第一个 x1 使得 [x1, i] 区间内的所有元素的和为 0。那么 [0, x1 - 1] 区间内的和是不是就是 sum[i] 了。于是这个问题就变成了——
找到在 [0, i - 1] 区间内,第一次出现 sum[i] 的位置即可。
我们不用真的初始化一个前缀和数组,因为我们只关心在 i 位置之前,第一个前缀和等于 sum[i] 的位置。因此,我们仅需用一个哈希表,一边求当前位置的前缀和,一边记录第一次出现该前缀和的位置。
1.3 算法实现
1.3.1 C++ 实现
class Solution {
public:
int findMaxLength(vector<int>& nums) {
unordered_map <int,int> hash; // 创建哈希,统计前缀和出现的位置
hash[0] = -1; // 默认有一个前缀和为 0 的情况
int sum = 0, ret = 0; // ret 标记最终长度
for(int i = 0; i < nums.size(); ++i) {
sum += nums[i] == 0 ? -1 : 1; // 三目表达式判断一下,是 0 就 -1
if(hash.count(sum)) { // 存在:如果找到 sum,说明此时 hash[sum] 里面存了前面那个的下标
ret = max(ret, i - hash[sum]); // 更新长度,i - j 即可
} else {
hash[sum] = i; // 记录第一次出现的位置
}
}
return ret;
}
};
时间复杂度:O(n),空间复杂度:O(n)。

1.3.2 Java 实现
class Solution {
public int findMaxLength(int[] nums) {
Map<Integer, Integer> hash = new HashMap<Integer, Integer>();
hash.put(0, -1); // 默认存在一个前缀和为 0 的情况
int sum = 0, ret = 0;
for (int i = 0; i < nums.length; i++) {
sum += (nums[i] == 0 ? -1 : 1); // 计算当前位置的前缀和
if (hash.containsKey(sum)) ret = Math.max(ret, i - hash.get(sum));
else hash.put(sum, i);
}
return ret;
}
}
时间复杂度:O(n),空间复杂度:O(n)。

032 矩阵区域和
题目描述:

2.1 算法思路:二维前缀和
二维前缀和的简单应用题,关键就是我们在填写结果矩阵的时候,要找到原矩阵对应区域的【左上角】以及【右下角】的坐标。
(1)左上角坐标:x1 = i - k,y1 = j - k,但是由于会「超过矩阵」的范围,因此需要对 0 取一个 max。因此修正后的坐标为:x1 = max(0, i - k),y1 = max(0, j - k);
(2)右下角坐标:x2 = i + k,y2 = j + k,但是由于会【超过矩阵】的范围,因此需要对 m - 1,以及 n - 1 取一个 min。因此修正后的坐标为:x2 = min(m - 1, i + k),y2 = min(n - 1, j + k)。

然后将求出来的坐标代入到【二维前缀和矩阵】的计算公式上即可,注意下标的映射关系。
2.2 算法实现
2.2.1 C++ 实现
class Solution {
public:
vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
int m = mat.size(), n = mat[0].size(); // 行和列
// 1、预处理一个前缀和矩阵
vector<vector<int>> dp(m + 1, vector<int>(n + 1)); // m + 1 行 n + 1 列,方便处理边界情况
// 填写矩阵
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
// 2、使用前缀和矩阵
vector<vector<int>> ret(m, vector<int>(n)); // 跟原始矩阵同等规模
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++) {
int x1 = max(0, i - k) + 1, y1 = max(0, j - k) + 1; // +1 就是为了在 dp 表里面直接可以找到
x2 = (m - , i + k) + , y2 = (n - , j + k) + ;
ret[i][j] = dp[x2][y2] - dp[x1 - ][y2] - dp[x2][y1 - ] + dp[x1 - ][y1 - ];
}
ret;
}
};
时间复杂度:O(mn),空间复杂度:O(mn)。

2.2.2 Java 实现
class Solution {
public int[][] matrixBlockSum(int[][] mat, int k) {
int m = mat.length, n = mat[0].length;
// 1、预处理前缀和矩阵
int[][] dp = new int[m + 1][n + 1];
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
// 2、使用
int[][] ret = new int[m][n];
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++) {
int x1 = Math.max(0, i - k) + 1, y1 = Math.max(0, j - k) + 1;
int Math.min(m - , i + k) + , y2 = Math.min(n - , j + k) + ;
ret[i][j] = dp[x2][y2] - dp[x1 - ][y2] - dp[x2][y1 - ] + dp[x1 - ][y1 - ];
}
ret;
}
}
时间复杂度:O(mn),空间复杂度:O(mn)。



