一.209.长度最小的子数组
题目链接:209. 长度最小的子数组 - 力扣(LeetCode)
文章讲解:代码随想录
视频讲解:拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili
滑动窗口问题的关键在于
1.用双指针进行初始化 我们用两个指针:left
和 right
,它们表示窗口的左右边界,初始化时都指向数组的起始位置。
2.设一个变量 current_sum
来记录当前窗口的和。
3.设一个变量 min_length
来记录当前找到的最短子数组的长度,初始化为一个很大的值(比如 n+1
,即数组的最大长度)。
4.扩大窗口,寻找终止扩大窗口的条件 current_sum >= target
5.收缩窗口,寻找终止收缩窗口的条件
同时更新 current_sum
,看看是否还能保持和大于等于 target,
每次收缩时,计算当前窗口的长度,更新 min_length
。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n=nums.size();
int left=0;
int right=0;
int min_length=INT_MAX;//将结果数组的长度初始化为最大
int cur_sum=0;//记录当前窗口数字的总和
for(right=0;right<n;right++)
{
cur_sum+=nums[right];
while(cur_sum>=target)
{
if((right-left+1)<min_length)
{
min_length=right-left+1;//更新符合条件的新窗口的长度
}
cur_sum-=nums[left];//先减去当前窗口的值
left++;//移动窗口左指针,缩减窗口
}
}
return min_length==INT_MAX?0:min_length;
}
};
易错点:1.
while(cur_sum>=target)
while
:用于多次收缩窗口,直到窗口的和小于target
。if
:只能在满足条件时收缩一次,没有办法多次收缩窗口,导致可能错过更小的符合条件的子数组。
易错点:2.
if((right-left+1)<min_length)
{
min_length=right-left+1;//更新符合条件的新窗口的长度
}
cur_sum-=nums[left];//先减去当前窗口的值
left++;//移动窗口左指针,缩减窗口
应该先更新当前窗口的长度,再收缩窗口
注意:可以自己先在草稿纸上演算
59.螺旋矩阵II
题目链接:59. 螺旋矩阵 II - 力扣(LeetCode)
文章讲解:代码随想录
视频讲解:一入循环深似海 | LeetCode:59.螺旋矩阵II_哔哩哔哩_bilibili
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> matrix(n,vector<int>(n,0));
int startx=0;
int starty=0;
int count=1;//用于计数
int offset=1;
int mid=n/2;
int loop=n/2;//转的圈数
int i,j;
while(loop--)
{
i=startx;
j=starty;
for(j=starty;j<n-offset;j++)
matrix[i][j]=count++;//遍历完成后,j=n-offset
for(i=startx;i<n-offset;i++)
matrix[i][j]=count++;
for(;j>starty;j--)
matrix[i][j]=count++;
for(;i>startx;i--)
matrix[i][j]=count++;
startx++;
starty++;
offset++;
}
if(n%2){
matrix[mid][mid]=count;}
return matrix;
}
};
易错点:1.记得保持循环不变量法则,一直保持左闭右开的填充数字
2.关于这段循环代码,因为填充的圈数不同,statX和starty的值会一直改变,最好不要用数字来代替。第一圈startx,starty为0,后面的圈数值会改变。
for(;j>starty;j--)
matrix[i][j]=count++;
for(;i>startx;i--)
matrix[i][j]=count++;
while(loop--)
{
i=startx;
j=starty;
for(j=starty;j<n-offset;j++)
matrix[i][j]=count++;//遍历完成后,j=n-offset
for(i=startx;i<n-offset;i++)
matrix[i][j]=count++;
for(;j>starty;j--)
matrix[i][j]=count++;
for(;i>startx;i--)
matrix[i][j]=count++;
startx++;
starty++;
offset++;
}
区间和(前缀和法)
文章讲解:58. 区间和 | 代码随想录
采用前缀和解题,多引用一个数组,记录区间累加和
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, a, b;
cin >> n;
vector<int> vec(n);
vector<int> p(n);
int presum = 0;
for (int i = 0; i < n; i++) {
cin >> vec[i];
presum += vec[i];
p[i] = presum; // 记录前缀和
}
while (cin >> a >> b) {
int sum;
if (a == 0) sum = p[b]; // 直接用前缀和计算
else sum = p[b] - p[a - 1]; // 区间和 = p[b] - p[a-1]
cout << sum << endl;
}
}
采用暴力算法解题
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, a, b;
cin >> n;
vector<int> vec(n);
for (int i = 0; i < n; i++) cin >> vec[i];
while (cin >> a >> b) {
int sum = 0;
// 累加区间 a 到 b 的和
for (int i = a; i <= b; i++) sum += vec[i];
cout << sum << endl;
}
}
时间复杂度
-
前缀和法:
- 预处理阶段:计算前缀和
p[]
的时间复杂度是 O(n),其中n
是数组的长度。 - 查询阶段:每次查询的时间复杂度是 O(1),因为通过前缀和可以直接得到区间和。
- 总体时间复杂度:O(n) + O(q),其中
q
是查询的次数。查询部分时间复杂度为 O(1),因此随着查询次数的增加,查询操作的效率很高。
- 预处理阶段:计算前缀和
-
暴力求解法:
- 查询阶段:每次查询都需要遍历区间
[a, b]
,时间复杂度为 O(b - a + 1)。如果有多个查询,这将导致查询的效率低下,尤其是当区间较大时,累加法的效率会急剧下降。
- 查询阶段:每次查询都需要遍历区间
开发商购买土地
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int main()
{
int n,m;
int sum=0;
cin>>n>>m;
vector<vector<int>> vec(n,vector<int>(m,0));
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>vec[i][j];
sum+=vec[i][j];
}}
vector<int>horizontal(n,0);
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
horizontal[i]+=vec[i][j];
}
}
vector<int> vertical(m,0);
for(int j=0;j<m;j++)
{
for(int i=0;i<n;i++)
{
vertical[j]+=vec[i][j];
}
}
int result=INT_MAX;
int horizontalCut=0;
for(int i=0;i<n;i++)
{
horizontalCut+=horizontal[i];
result=min(result,abs(sum-horizontalCut-horizontalCut));//不断更新最小值
}
int verticalCut=0;
for(int j=0;j<m;j++ )
{
verticalCut+=vertical[j];
result=min(result,abs(sum-verticalCut-verticalCut));
}
cout<<result<<endl;
}
解题思路:
-
输入和初始化:
- 输入矩阵的大小
n
和m
,并读取矩阵中的所有值。 - 同时计算出整个矩阵的总和
sum
,这个值将在后续计算中用于判断差值。
- 输入矩阵的大小
-
计算每行和每列的和:
- 横向划分:即按行进行划分。我们需要先计算每行的总和,保存在
horizontal
数组中。对于每个i
,horizontal[i]
表示第i
行的元素和。 - 纵向划分:即按列进行划分。计算每列的总和,保存在
vertical
数组中。对于每个j
,vertical[j]
表示第j
列的元素和。
- 横向划分:即按行进行划分。我们需要先计算每行的总和,保存在
-
进行横向划分并更新最小差值:
- 横向划分时,将矩阵划分为上部分和下部分。通过累加每一行的和,逐步计算出不同的划分方式。每次计算一个分割点后,我们可以得到上部分和下部分的总和差值。
result
初始设为最大值INT_MAX
,每次计算出一个新的差值后,更新result
,得到最小差值。
-
进行纵向划分并更新最小差值:
- 纵向划分时,将矩阵划分为左部分和右部分。通过累加每一列的和,逐步计算不同的纵向分割方式。每次计算一个分割点后,同样计算左右部分的总和差值。
- 同样,每次更新
result
,保证得到最小的差值。
-
输出结果:
最后输出最小的差值,即所求的最小差距。复杂度分析:
- 时间复杂度:O(n * m),因为我们需要遍历矩阵中的每个元素来计算总和、每行和每列的和。然后再进行横向和纵向的遍历来更新最小差值。
- 空间复杂度:O(n + m),因为需要分别存储每行的和和每列的和。