Day02| 209.长度最小的子数组、59.螺旋矩阵II、58.区间和、44.开发商购买土地、数组总结

2025-07-10

209.长度最小的子数组

题目链接:209.长度最小的子数组
文档讲解:代码随想录
状态:暴力解决,学会滑动窗口法

思路

本题关键点在于最小子串正整数元素

初始的想法:暴力解法,第一层for循环选择字串起点,第二层遍历寻找终点,不断比较更新得到结果。

滑动窗口法:维护两个指针,根据单调性,想找到解必须尾指针向右移动(sum增加),想确定这个解是不是这一大部分的最优解,一定是头指针向右移动(都是整数,sum想减小必须向右),遍历两次即可得解。

双指针移动图解:

chart

关键代码图解:

chart

题解

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) 
    {//暴力解法
    //     int result = INT32_MAX;
    //     for(int i = 0;i<nums.size();i++)
    //     {
    //         int sum = 0,temp = 0;
    //         for(int j = i;j<nums.size();j++)
    //         {
    //             sum += nums[j];
    //             temp++;
    //             if(sum >= target)result = temp<result?temp:result;
    //         }
    //     }
    //     if(result == INT32_MAX)return 0;
    //     else return result;
    // }
     //双指针,利用正数单调性
        int result = INT32_MAX, sum = 0, temp = 0;
        for(int j = 0,i = 0;j<nums.size();j++)
        {
            sum += nums[j];
            while(sum>=target)
            {
                temp = j-i+1;
                result = temp<result?temp:result;
                sum -= nums[i++];
            }
        }
        if(result == INT32_MAX)return 0;
        else return result;
    }
};

59.螺旋矩阵II

题目链接:59.螺旋矩阵II
文档讲解:代码随想录
状态:不会做,学习题解解决,感觉很精妙

思路

一看题目就知道围绕二维数组做文章,首先知道vector创建二维数组的方式vector<vector<int>> vec(3, vector<int>(4));->创建3×4数组

此题没有什么经典算法,但是学习题解大有感悟,如下图所示,数组行列之间没有规律,仅能螺旋赋值,有四种方式。

代码随想录中的题解太过于啰嗦,转几圈,每圈位置如何递减,奇偶区别等,有没有一劳永逸的方法?有!四种方向用数组表示DIRS[4][2]搭配i+DIRS[di][0]j+DIRS[di][1]就实现了全向移动。之前最麻烦的循环条件现在转变为了边界处理,岂不简单?x<0 || x>=n || y<0 || y>=n || ans[x][y]不越界,有位置就能为所欲为。

chart

题解

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) 
    {
        int DIRS[4][2] = {[0,1],[1,0],[0,-1],[-1,0]};//网站格式原因,将{}写为[]
        vector ans(n,vector<int>(n));
        int i = 0,j = 0,di = 0;
        for(int val = 1;val<=n*n;val++)
        {//赋值
            ans[i][j] = val;
        //判断下一位置
            int x = i+DIRS[di][0];
            int y = j+DIRS[di][1];
            if(x<0 || x>=n || y<0 || y>=n || ans[x][y]) di = (di+1)%4;
        //移动
            i = i+DIRS[di][0];
            j = j+DIRS[di][1];
        }
        return ans;
    }
};

58.区间和

题目链接:58.区间和
文档讲解:代码随想录
状态:暴力轻松解决,前缀和瞥了一眼思路轻松解决

思路

ACM模式不用说了,正是拿手的地方

前缀和思想主要应用于区间和问题,正常暴力解法极其简单,但是n数组m次搜索,复杂度O(mn)稍大些(话说这么简单就是不太对头)。优化思路总结贯通为前缀和(复杂度O(n)),如下图所示,想统计vec数组上25下标之间的累加和,那是不是就用 p[5] - p[1] 就可以了。

chart

可谓一劳永逸

题解

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    //暴力解法
    // vector<int> vec;
    // int n = 0;
    // cin>>n;
    // while(n--)
    // {
    //     int x = 0;
    //     cin>>x;
    //     vec.push_back(x);
    // }
    // int start = 0, end = 0;
    // while(cin>>start>>end)
    // {
    //     int sum = 0;
    //     for(int i = start;i<=end;i++)
    //     {
    //         sum += vec[i];
    //     }
    //     cout<<sum<<endl;
    // }
    // return 0;
    //前缀和
    vector<int> vec;
    int n = 0;
    cin>>n;
    for(int i = 0;i<n;i++)cin>>vec[i];
    int start = 0, end = 0;
    while(cin>>start>>end)
    {
        int sum = 0;
        for(int i = start;i<=end;i++)
        {
            sum += vec[i];
        }
        cout<<sum<<endl;
    }
    return 0;
}

44.开发商购买土地

题目链接:44.开发商购买土地
文档讲解:代码随想录
状态:不会做,但是理清题解清楚了

思路

不考虑暴力解法

优化版本把这个题目看作模拟,关键点是整体sum部分做差比较

本题的多维前缀和其实是针对于求解前两行、前三行的和(可看作区间和),但是本题本质不用多次求解,故前缀和没有起到太大方便作用。

chart

题解

#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main()
{   // 模拟解法
    // int n = 0, m = 0, sum = 0, result = INT_MAX;
    // 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];
    //     }
    // }
    // //行确定
    // int temp_sum = 0;
    // for(int i = 0;i<n;i++)
    // {
    //     for(int j = 0;j<m;j++)
    //     {
    //         temp_sum += vec[i][j];
    //         if(j == m-1)result = abs(sum-temp_sum-temp_sum)<result?abs(sum-temp_sum-temp_sum):result;//用j == m-1作为行分割
    //     }
    // }
    // //列确定
    // temp_sum = 0;
    // for(int j = 0;j<m;j++)
    // {
    //     for(int i = 0;i<n;i++)
    //     {
    //         temp_sum += vec[i][j];
    //         if(i == n-1)result = abs(sum-temp_sum-temp_sum)<result?abs(sum-temp_sum-temp_sum):result;//用i == n-1作为列分割
    //     }
    // }
    // cout<<result<<endl;
 
    //前缀和法
    int n = 0, m = 0, sum = 0, result = INT_MAX;
    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> vex(n,0);
    for(int i = 0;i<n;i++)
    {
        int temp_sum = 0;
        for(int j = 0;j<m;j++)
        {
            temp_sum += vec[i][j];
        }
        if(i == 0)vex[i] = temp_sum;
        else vex[i] = temp_sum + vex[i-1];
    }
    vector<int> vey(m,0);
    for(int j = 0;j<m;j++)
    {
        int temp_sum = 0;
        for(int i = 0;i<n;i++)
        {
            temp_sum += vec[i][j];
        }
        if(j == 0)vey[j] = temp_sum;
        else vey[j] = temp_sum += vey[j-1];
    }
 
    for(int i = 0;i<n;i++) result = min(abs(sum-vex[i]-vex[i]), result);
    for(int j = 0;j<m;j++) result = min(result, abs(sum-vey[j]-vey[j]));
    cout<<result<<endl;
    return 0;
}

数组总结

做了一些数组的题目,我的感受是数组的关键点在于玩好下表,不管是二分、删除(覆盖)、双指针、螺旋,难点都在于如何管理好代表下标的指针,以及边界条件的判断

还要重点关注数组中元素的性质,正or负、有序or无序等等,灵活选择头/尾或者中间下手处理数组,充分利用好题目条件。