代码随想录day02

一.209.长度最小的子数组

题目链接:209. 长度最小的子数组 - 力扣(LeetCode)

文章讲解:代码随想录

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

滑动窗口问题的关键在于

1.用双指针进行初始化     我们用两个指针:leftright,它们表示窗口的左右边界,初始化时都指向数组的起始位置。

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)。如果有多个查询,这将导致查询的效率低下,尤其是当区间较大时,累加法的效率会急剧下降。

开发商购买土地

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

#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;
    
}

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

解题思路:

  1. 输入和初始化

    • 输入矩阵的大小 nm,并读取矩阵中的所有值。
    • 同时计算出整个矩阵的总和 sum,这个值将在后续计算中用于判断差值。
  2. 计算每行和每列的和

    • 横向划分:即按行进行划分。我们需要先计算每行的总和,保存在 horizontal 数组中。对于每个 ihorizontal[i] 表示第 i 行的元素和。
    • 纵向划分:即按列进行划分。计算每列的总和,保存在 vertical 数组中。对于每个 jvertical[j] 表示第 j 列的元素和。
  3. 进行横向划分并更新最小差值

    • 横向划分时,将矩阵划分为上部分和下部分。通过累加每一行的和,逐步计算出不同的划分方式。每次计算一个分割点后,我们可以得到上部分和下部分的总和差值。
    • result 初始设为最大值 INT_MAX,每次计算出一个新的差值后,更新 result,得到最小差值。
  4. 进行纵向划分并更新最小差值

    • 纵向划分时,将矩阵划分为左部分和右部分。通过累加每一列的和,逐步计算不同的纵向分割方式。每次计算一个分割点后,同样计算左右部分的总和差值。
    • 同样,每次更新 result,保证得到最小的差值。
  5. 输出结果

    最后输出最小的差值,即所求的最小差距。

    复杂度分析:

  6. 时间复杂度:O(n * m),因为我们需要遍历矩阵中的每个元素来计算总和、每行和每列的和。然后再进行横向和纵向的遍历来更新最小差值。
  7. 空间复杂度:O(n + m),因为需要分别存储每行的和和每列的和。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值