209. 长度最小的子数组
第一想法
看到这道题的第一想法是做一个前缀和,但是引入前缀和之后需要去减去之后恰好小于target的另一个前缀和,遍历的话复杂度就会变成 $O(n^2)$ ,然后想到了可以二分查找;
但是仔细思考一下就会发现,可以维护一个队列,不断把数据push进去,一旦找到大于target的就开始把前面的数据踢出去,直到再次小于target;这样就不需要复杂的查找了;
看完题解后的想法
队列在这里其实就是一个双指针维护的滑动窗口;
实现中遇到的困难
result需要判断当前窗口大小和历史result谁更小,使用双指针滑动窗口不会有问题,但是如果是用队列来维护滑动窗口,需要使用queue.size(),返回的类型是size_t,需要强制转换成int;
一开始还给result直接赋值0,但是这样在min里会锁死最小值;
代码
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int sum = 0;
int result = INT_MAX;
int left = 0;
for (int right = 0; right < nums.size(); right++) {
sum += nums[right];
while (sum >= target) {
result = min(result, right-left+1);
sum = sum - nums[left];
left++;
}
}
return result == INT_MAX ? 0 : result;;
}
};
59. 螺旋矩阵II
第一想法
看完题目的第一想法就是如何移动坐标去填充数据,然后就被边界条件卡住了;
看完题解后的想法
比起移动坐标本身,需要移动的是边界,填充不是让坐标自己去找边界,而是让坐标在边界上移动;
实现中遇到的困难
沿四条边界进行填充时,需要注意移动方向和限制条件;
代码
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> result(n, vector<int>(n, 0));
int top = 0, down = n-1;
int left = 0, right = n-1;
int target = n * n;
int num = 1;
// 填充四个边界,每填充一个边界就把边界向内移动
while (num <= target) {
// 1. 填充上边界
for (int j = left; j <= right; j++) {
result[top][j] = num;
num++;
}
top++;
// 2. 填充右边界
for (int i = top; i <= down; i++) {
result[i][right] = num;
num++;
}
right--;
// 3. 填充下边界
for (int j = right; j >= left; j--) {
result[down][j] = num;
num++;
}
down--;
// 4. 填充左边界
for (int i = down; i >= top; i--) {
result[i][left] = num;
num++;
}
left++;
}
return result;
}
};
58. 区间和
- 链接:KamaCoder 58
- 文章:代码随想录
- 状态:⚠️
第一想法
区间和就是前缀和的差
在数组问题里只要看到连续的某一部分的时候优先考虑前缀和;
看完题解后的想法
——
实现中遇到的困难
ACM输入输出忘了咋实现了;
注意因为是a到b的区间,所以下标应该是 b - (a - 1);
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, a, b;
cin >> n;
vector<int> input(n);
vector<int> prevs(n);
int prev = 0;
for (int i = 0; i < n; i++) {
cin >> input[i];
prev += input[i];
prevs[i] = prev;
}
while (cin >> a >> b) {
int result = 0;
if (a == 0) {
result = prevs[b];
} else {
result = prevs[b] - prevs[a-1];
}
cout << result << endl;
}
return 0;
}
44. 开发商购买土地
- 链接:KamaCoder 44
- 文章:代码随想录
- 状态:❌
第一想法
被分块这个行为卡住了没能理解怎么做;
看完题解后的想法
只能横竖分块其实就是计算每行的和与每列的和;然后再按照前缀和的思路来计算块与块之间的差;
最后一项前缀和就是总量, $$Diff = A - (Total - A) = A - Total + A = 2A - Total$$
实现中遇到的困难
——
代码
#include <iostream>
#include <vector>
#include <cmath>
#include <climits>
#include <algorithm>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
// 1. 建图
vector<vector<int>> map(n, vector<int>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> map[i][j];
}
}
// 2. 统计每行的前缀和
vector<int> rows(n, 0);
int row_sum = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
row_sum += map[i][j];
}
rows[i] = row_sum;
}
// 3. 统计每列的前缀和
vector<int> cols(m, 0);
int col_sum = 0;
for (int j = 0; j < m; j++) {
for (int i = 0; i < n; i++) {
col_sum += map[i][j];
}
cols[j] = col_sum;
}
// 4. 计算按行分和按列分的最小差值
int result = INT_MAX;
for (int i = 0; i < n - 1; i++) {
result = min(result, abs((rows[n-1] - rows[i])- rows[i]));
}
for (int j = 0; j < m - 1; j++) {
result = min(result, abs((cols[m-1] - cols[j])- cols[j]));
}
cout << result << endl;
return 0;
}
今日收获
深入使用了前缀和,强化了双指针滑动窗口的使用;
螺旋矩阵的让边界自己移动的思路要记住;