第二天|209 长度最小子数组 59.螺旋矩阵 区间和 开发商 数组总结

第二天|209 长度最小子数组 59.螺旋矩阵 区间和 开发商 数组总结

209.长度最小子数组

笔记

拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili

209.长度最小的子数组 | 代码随想录

暴力解法:

两个循环,用不同的开头进行的遍历,不断地更新最小子数组得到结果。代码如下:

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.螺旋矩阵 ||

笔记

59.螺旋矩阵II | 代码随想录

一入循环深似海 | 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;

    }
};

区间和

58. 区间和 | 代码随想录

笔记

暴力解法时间复杂度是 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,耗时会小很多

开发商购买土地

44. 开发商购买土地 | 代码随想录

笔记

可以和上边“区间和”类比

使用前缀和的思路,先将行方向和列方向的前缀和分别求出来,再遍历所有切法,统计最小差值

实操出现问题:

写数组题目经常出现溢出的报错,目前无一例外错误都是把数组大小当成最后一个元素的索引,需要注意。

代码/比较:

我自己的:

#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.前缀和:存入从该数据开始往前的元素和。

posted @ 2026-01-15 19:40  欧尼酱ovo  阅读(0)  评论(0)    收藏  举报