第二天|209 长度最小子数组 59.螺旋矩阵 区间和 开发商 数组总结
第二天|209 长度最小子数组 59.螺旋矩阵 区间和 开发商 数组总结
209.长度最小子数组
笔记
拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili
暴力解法:
两个循环,用不同的开头进行的遍历,不断地更新最小子数组得到结果。代码如下:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result=INT32_MAX;
int sum=0;//求和
int len=0;//子数组长度
for(int i=0;i<nums.size();i++)//遍历
{
sum=0;//每次循环都要清零
for(int j=i;j<nums.size();j++)
{
sum=sum+nums[j];
if(sum>=target)//如果和大于target,更新result
{
len=j-i+1;//求本次的子数组长度
//判断:本次循环找到的是否是比当前result更小?如果不是则保留
result=result>len?len:result;
break;//因为找的是最小,所以直接结束循环
}
}
}
return result==INT32_MAX?0:result;
}
};
实操出现问题:
1.因为要找的是最小的子数组,所以找到一个子数组符合sum>=target的条件即可直接break
2.用三目运算符更简化代码
3.result=INT32_MAX
滑动窗口解法:
for循环的j表示的是窗口的截止位置;
起始位置如何移动?如果此时窗口内的数字和大于等于target了,起始位置前移,直到窗口内数字和小于target为止(所以要使用while循环而不是for循环),再继续移动截止位置。
为什么能够遍历完全??
时间复杂度:看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
实操出现的问题
起始位置i++什么时候更新:完成子序列长度计算和sum计算后才更新。
代码/比较
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int sum=0;//表示窗口内的数字和
int len=0;//表示符合条件的子数组长度
int result=INT32_MAX;//表示结果,即最小的子数组长度
int i=0;//滑动窗口的起始位置
for(int j=0;j<nums.size();j++)
{
sum=sum+nums[j];
while(sum>=target)//调整起始位置
{
sum=sum-nums[i];
len=j-i+1;//计算子数组长度
i++;
result=len<result?len:result;//更新result,判断是否小于现有的结果,如果更小则更新,否则不更新
}
}
return result==INT32_MAX?0:result;
}
};
59.螺旋矩阵 ||
笔记
一入循环深似海 | LeetCode:59.螺旋矩阵II_哔哩哔哩_bilibili
循环不变量:循环的过程中对于边界处理的规则都是一样的,坚持左闭右开的原则
循环条件:只用转动n/2圈,为什么?因为转一圈相当于省去两行/两列
流程:一条边一条边的遍历并填充数据进入二位数组;如果n为奇数最后处理中间元素;遍历过程中完成一圈后减去偏移量即可进行第二个圈的遍历
实操出现问题
1.二维数组的初始化: vector<vector<int>> nums(n,vector<int>(n));
2.注意数组索引。
代码/比较
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> nums(n,vector<int>(n));//二维数组
int startx=0;
int starty=0;//横纵方向索引的开始
int circle=0;//转的圈数
int staff=1;//偏移量,转完一圈后修改起始位置
int count=1;//用于计数,自增
while(circle<n/2)//转的圈数小于n/2,circle完成一个while循环后自增
{
int i=0;
int j=0;
for(j=starty;j<n-staff;j++)
nums[startx][j]=count++;
for(i=startx;i<n-staff;i++)
nums[i][j]=count++;
for(;j>starty;j--)
nums[i][j]=count++;
for(;i>startx;i--)
nums[i][j]=count++;
circle++;
staff++;
startx++;
starty++;
}
if(n%2!=0)//如果是奇数,额外处理中间的元素
{
nums[n/2][n/2]=n*n;
}
return nums;
}
};
区间和
笔记
暴力解法时间复杂度是 O(n * m) m 是查询的次数;
使用前缀和的方法;
注意下标问题,如果要求的是2-5内的数据和,应该使用p[5]-p[1]而不是p[5]-p[2]
注意数组索引0
实操出现的问题
cout<< cin>>
代码/比较
我自己的:
#include <iostream>
using namespace std;
#include <vector>
int main()
{
int n;//整数数组array的长度
int a,b;//区间
cin>>n;
vector <int> nums(n);
vector <int> sum(n);//前缀和
for(int i=0;i<n;i++)//输入数组长度、计算前缀和
{
cin>>nums[i];
if(i==0)
{
sum[i]=nums[i];
}
sum[i]=sum[i-1]+nums[i];
}
while(cin>>a>>b)//输出
{
int result=0;
if(a==0)
{
result=sum[b];
}
result=sum[b]-sum[a-1];
cout<<result<<endl;
}
}
其他:
#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++) {
scanf("%d", &vec[i]);
presum += vec[i];
p[i] = presum;
}
while (~scanf("%d%d", &a, &b)) {
int sum;
if (a == 0) sum = p[b];
else sum = p[b] - p[a - 1];
printf("%d\n", sum);
}
}
C++ 代码 面对大量数据 读取 输出操作,最好用scanf 和 printf,耗时会小很多
开发商购买土地
笔记
可以和上边“区间和”类比
使用前缀和的思路,先将行方向和列方向的前缀和分别求出来,再遍历所有切法,统计最小差值
实操出现问题:
写数组题目经常出现溢出的报错,目前无一例外错误都是把数组大小当成最后一个元素的索引,需要注意。
代码/比较:
我自己的:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main()
{
int m, n;//数组大小,n行m列
cin >> n >> m;
vector<vector<int>> nums(n, vector<int>(m));//二维数组
for (int i = 0; i < n; i++)//输入数组
{
for (int j = 0; j < m; j++)
{
cin >> nums[i][j];
}
}
vector<int> hengxiang(n);//横向统计,每一列行之前的数据和
int sum = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
sum += nums[i][j];
}
hengxiang[i] = sum;
}
vector<int> zongxiang(m);//纵向统计,每一列之前的数据和
sum = 0;
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
sum += nums[j][i];
}
zongxiang[i] = sum;
}
//总和相等hengxiang[n-1]=zongxiang[m-1]
int result = INT32_MAX;
for (int i = 1; i < n; i++)//横着切
{
int hengxiang_cut = hengxiang[i - 1];//上半部分和
int hengxiang_rest = hengxiang[n-1] - hengxiang_cut;//下半部分和
int sub = abs(hengxiang_cut - hengxiang_rest);//差值
result = result < sub ? result : sub;
}
for (int i = 1; i < m; i++)//竖着切
{
int zongxiang_cut = zongxiang[i - 1];//左半部分和
int zongxiang_rest = zongxiang[m-1] - zongxiang_cut;//右半部分和
int sub = abs(zongxiang_cut - zongxiang_rest);//差值
result = result < sub ? result : sub;
}
cout << result;
return 0;
}
其他:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
int sum = 0;
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];
}
}
int result = INT_MAX;
int count = 0; // 统计遍历过的行
for (int i = 0; i < n; i++) {
for (int j = 0 ; j < m; j++) {
count += vec[i][j];
// 遍历到行末尾时候开始统计
if (j == m - 1) result = min (result, abs(sum - count - count));
}
}
count = 0; // 统计遍历过的列
for (int j = 0; j < m; j++) {
for (int i = 0 ; i < n; i++) {
count += vec[i][j];
// 遍历到列末尾的时候开始统计
if (i == n - 1) result = min (result, abs(sum - count - count));
}
}
cout << result << endl;
}
可以不使用前缀和,在行向/纵向遍历的时候进行末尾统计,代码更简练。
数组总结
1 二分法:注意区间:左闭右闭/左闭右开;循环不变量;
2.双指针移除元素:满指针指向的是新数组(修改后),快指针指向的是旧数组(修改前),新数组不需要那个”需要被删除的元素“;
3.双指针有序数组平方:注意二维数组、一维数组的初始化;
4.长度最小的子数组:for循环里面更新的是滑动窗口的末尾;什么时候更新起始位置:当窗口内的和比预设值小的时候更新
5.螺旋矩阵:循环不变量
6.前缀和:存入从该数据开始往前的元素和。

浙公网安备 33010602011771号