算法第二天|滑动窗口:LeetCode209长度最小的子数组、螺旋矩阵:LeetCode59螺旋矩阵Ⅱ、前缀和:KamaCoder58区间和、KamaCoder44开发商购买土地

滑动窗口思路总结:

思路一:数组模拟双端队列

        步骤1:定义一个数组(大小看题目中的窗口大小),用于表示双端队列,记录窗口内元素的下标。

        步骤2:定义双端队列的队头、队尾(队头hh从0开始,队尾tt从-1开始),hh++表示将当前窗口从队头向后移动一位(减少窗口元素数量),++tt表示窗口从队尾向后移动一位(增加窗口元素数量)。

        步骤3:定义与题相关的某些变量

        步骤4:开始for循环遍历数组

        步骤5:通过对双端队列中的队头队尾进行相应的++--,找到对应的满足的窗口,记录对应的值 

思路二:使用一个变量记录窗口的起始位置,遍历数组,不断调整窗口

LeetCode209、长度最小的子数组

题目链接:209、长度最小的子数组

1、使用双端队列记录窗口

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        //使用数组模拟双端队列解题
        //定义双端队列数组,用于记录窗口中的下标
        int q[100010];
        //定义双端队列的队头、队尾
        int hh=0,tt=-1;//使用队尾=-1,每次向双端队列中添加元素就使++tt,出队就hh++;
        //定义窗口中的元素和:
        int sum = 0;
        //定义窗口中的元素个数
        int n = nums.size()+1;//当最后是这个数值返回0,不是这个数值返回最小的

        //开始遍历数组,向双端队列中添加数组下标
        for(int i=0;i<nums.size();i++)
        {
            //向窗口中添加当前的新元素
            sum +=nums[i];
            //增加窗口的长度
            q[++tt] = i;
            //判断窗口元素和是不是满足sum>=target,同时双端队列不为空
            while(sum>=target&&hh<=tt)
            {
                //窗口中的元素和大于等于target,需要记录窗口中的元素个数、去掉队头一位
                n = n<=q[tt]-q[hh]+1?n:q[tt]-q[hh]+1;//记录窗口当前的元素数
                
                sum -=nums[q[hh]];//记录之后的sum和
                hh++;//队头移动一位
            }
            //此时已经不满足sum>=target,窗口可以向后移动了
        }
        return n==nums.size()+1?0:n;   
    }
};

2、使用变量i记录窗口起始位置 

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        //不使用双端队列记录窗口,使用一个变量记录当前窗口的起始位置

        //定义一个窗口的起始位置
        int i=0;
        //定义滑动窗口的元素和
        int sum = 0;
        //定义滑动窗口的长度
        int length =0;
        //定义最小数量
        int res = nums.size()+1;
        //使用for循环遍历
        for(int j=0;j<nums.size();j++)
        {
            sum +=nums[j];
            while(sum>=target)
            {
                //获取窗口元素数量
                length =j-i+1;
                //减去窗口起始位置的元素,窗口向后走
                sum -=nums[i];
                res = res<length?res:length;
                i++;
            }
        }
        return res==nums.size()+1?0:res;
        
    }
};

螺旋矩阵思路总结:

主要思想:让值能够顺序的填入,在需要转弯的地方设置转弯条件

转弯条件:

        1、当向右水平移动时(x不变,y增加),在最后会出界,需要向下转弯

        2、当向下竖直移动时(x增减,y不变),在最后会出界,需要向左转弯

        3、当向左水平移动时(x不变,y减少),在最后会出界,需要向上转弯

        3、当向上竖直移动时(x减少,y不变),在最后会覆盖(0,0),需要向右转弯

 综上,需要设置的转弯条件有:

        1、y的下一个值>=n时,向下转弯

        2、x的下一个值>=n时,向左转弯

        3、y的下一个值<0时,向上转弯

        4、之后,如果下一个位置已经存在填入的值,就向右、向下、向左、向上、、、循环

思路:

对x轴、y轴设置偏移量,dx、dy

        dx[4]={0,1,0,-1}

        dy[4]={1,0,-1,0}

使用(x+dx[d],y+dy[d])的形式获取下一个(x,y)坐标,当下一个位置触发转弯限制,就将d增加1

        d = (d+1)%4

 LeetCode59、螺旋矩阵Ⅱ

题目链接:螺旋矩阵Ⅱ

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {

        //定义一个返回数组res,大小是(n,n),初始是0
        vector<vector<int>>res(n,vector<int>(n,0));
        //定义当前位置:
        int x=0,y=0,d=0;
        //定义偏移矩阵(垂直、水平)
        int dx[4]={0,1,0,-1};
        int dy[4]={1,0,-1,0};

        //使用for循环遍历从1-n*n
        for(int i=1;i<=n*n;i++)
        {
            //填入当前位置的值
            res[x][y] = i;
            //设置转弯位置的限制
            //1、下一个位置出界>=n,<0
            //2、下一个位置已经存在值了
            if(y+dy[d]>=n||x+dx[d]>=n||y+dy[d]<0||res[x+dx[d]][y+dy[d]]!=0)
            {
                //要转弯,就将偏移值移动到下一位
                d =(d+1)%4;//确保一直是0,1,2,3
            }
            //更新位置
            x +=dx[d];
            y +=dy[d];
        }
        //返回矩阵
        return res;
        
    }
};

前缀和思路总结:

思路1(当输入是自己设置,较为合适:循环从1开始):

        1、使用公式A[i] = A[i-1]+Array[i]来计算前缀和

        2、计算相应的区间和:res = A[b]-A[a-1];

注意点:

        1、循环要从1开始for(int i=1;i<=nums.size(),i++)

        2、如果要计算的是下标的区间和需要使用res =A[b+1]-A[a];

思路2(输入不是自己设置,是给好的数组时:循环从0开始)

        1、同样使用公式A[i] = A[i-1]+Array[i],但是因为存在0-1=-1,需要使用中间变量获取元素的和,再存入对应的前缀和数组的对应位置

int presum=0;//定义中间变量
for()
{
    presum +=Array[i]记录当前位置的和(注意从下标0开始)
    A[i] = presum;
}

        2、计算区间和需要判断(所以喜欢使用第一种思路)

if(a==0)res = A[b]
else if(a!=0)res = A[b]-A[a-1]

 

KamaCoder58、区间和

题目链接:KamaCoder58、区间和

//使用思路1
//循环从1开始,
//直接使用公式 A[i] = A[i-1]+Array[i];计算区间和
//最后输出需要判断l,r是不是下标,是下标需要 res = A[b+1]-A[a];
#include <iostream>

const int N =100010;
int Array[N];
int A[N];//前缀和数组
using namespace std;

int main()
{
    int n;
    scanf("%d",&n);
    //获取数组
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&Array[i]);
    }

    //计算前缀和
    for(int i=1;i<=n;i++)
    {
        A[i] = A[i-1]+Array[i];
    }
    //计算需要的区间和
    int l,r;
    while(cin>>l,cin>>r)
    {
        printf("%d\n",A[r+1]-A[l]);//因为是下标,需要加1
    }
    return 0;
}

//使用思路二
//循环从0开始,加入中间变量presum来获取元素和,再赋值给A[i]
//最后计算区间和需要判断左端点是不是为0
#include <iostream>

const int N =100010;
int Array[N];
int A[N];//前缀和数组
using namespace std;

int main()
{
    int n;
    scanf("%d",&n);
    //获取数组
    for(int i=0;i<n;i++)
    {
        scanf("%d",&Array[i]);
    }

    //计算前缀和
    int presum=0;
    for(int i=0;i<n;i++)
    {
        presum +=Array[i];

        A[i] = presum;
    }
    //计算需要的区间和
    int l,r;
    while(cin>>l,cin>>r)
    {
        if(l==0)
        printf("%d\n",A[r]);//需要判断是不是为0
        else
        printf("%d\n",A[r]-A[l-1]);

    }
    return 0;
}

KamaCoder44、开发商购买土地

题目链接:KamaCoder44、开发商购买土地

思路:

        分竖直、水平分别计算前缀和,遍历前缀和,计算左右、上下两段的区间和,寻找最小值

//这道题目使用前缀和
//分别计算出行的前缀和数组、列的前缀和数组
//使用for循环遍历前缀和数组,获取从[0,i],[i+1,end]的差值,取最小值

#include <iostream>
#include <vector>
#include <climits>//定义INT_MAX
using namespace std;

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    //前缀和使用思路1的从1开始计算
    vector<vector<int>> Array (n+1 , vector<int>(m+1,0));
    //输入数组Array
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&Array[i][j]);
        }
    }

    //获取每一行的和
    vector<int>hang(n+1,0);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            hang[i] +=Array[i][j];
        }
    }

    //获取每一列的和
    vector<int>lie(m+1,0);
    for(int j=1;j<=m;j++)
    {
        for(int i=1;i<=n;i++)
        {
            lie[j] +=Array[i][j];
        }
    }

    //获取行的前缀和数组
    vector<int>hang_s(n+1,0);
    for(int i=1;i<=n;i++)
    {
        hang_s[i] = hang_s[i-1]+hang[i];
    }
    //获取列的前缀和数组
    vector<int>lie_s(m+1,0);
    for(int i=1;i<=m;i++)
    {
        lie_s[i] = lie_s[i-1]+lie[i];
    }

    //开始判断哪种最小
    int res=INT_MAX;
    for(int i=1;i<=n;i++)
    {
        res = min(res,abs((hang_s[i]-hang_s[0])-(hang_s[n]-hang_s[i])));
    }
    for(int i=1;i<=m;i++)
    {
        res = min(res,abs((lie_s[i]-lie_s[0])-(lie_s[m]-lie_s[i])));
    }
    printf("%d",res);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值