[代码随想录02]数组长度最小的子数组、螺旋矩阵、区间和、开发商购买土地

前言

 滑动窗口他是双指针的兄弟,咱们可以理解为一种有条件的双指针,他的两个指针被创建出来就是为了执行一个任务,这个任务可能是统计指针间的元素之和,亦或者是达到某一个条件不能就得换一种操作执行,然后又换回来,类似与取数据和收数据,收满了就得取,取空了就得收

前缀和:前缀和的思想是重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数。前缀和 在涉及计算区间和的问题时非常有用!本质上就是空间换时间的做法,这个类似的做法在二叉树的递归操作里面很常见。

题目链接

 209. 长度最小的子数组 - 力扣(LeetCode)

59. 螺旋矩阵 II - 力扣(LeetCode)

58. 区间和(第九期模拟笔试)

44. 开发商购买土地(第五期模拟笔试)

一、长度最小的子数组

 最简单的滑动窗口模型:所有滑动窗口的基础,固定一个左值向里面放数据,达到条件就进行更新操作,我们其实可以直接双循环把所有的情况都列举出来,然后我们来进行选择,但是这样做的时间复杂度过高,滑动窗口就是可以直接排除一些不必要的循环,举一个简单的例子。

//暴力解法
    int minSubArrayLen(int target, vector<int>& nums) {
        int n=nums.size(),ans=INT32_MAX,sum=0;
        for(int i=0;i<n;i++){
            sum=0;
            for(int j=i;j<n;j++){
                sum+=nums[j];
                if(sum>=target){
                    ans=min(ans,j-i+1);
                    break;
                }
            }
        }
        return ans<n?ans:0;
    }

    int minSubArrayLen1(int target, vector<int>& nums) {
        int n=nums.size(),ans=INT32_MAX,sum=0,left=0;
        for(int right=0;right<n;right++){
            sum+=nums[right];
            while(sum>=target){
                ans=min(ans,right-left+1);
                sum-=nums[left++];
            }//内层循环更新数据
        }
        return ans<=n?ans:0;
    }
    int minSubArrayLen2(int target, vector<int>& nums) {
        int n=nums.size(),ans=INT32_MAX,left=0,sum=0;
        for(int right=0;right<n;right++){
            sum+= nums[right];
            while(sum-nums[left]>=target) sum-=nums[left++];
            if(sum>=target)ans=min(ans,right-left+1);
        }//外层循环处理问题
        return ans<=n?ans:0;
    }

 提升题目:水果成篮904. 水果成篮 - 力扣(LeetCode)

思路:哈希表(数组)和滑动窗口的结合,利用哈希表进行词频统计,用滑动窗口的思路去解答题目的意思。记录每种水果的数量,每次固定左端点,同时移动右端点,直到范围内的水果种数超过2,此时更新答案,左端点每次次循环只移动一个单位。

int totalFruit(vector<int>& fruits) {
        //思路哈希加滑动窗口
       unordered_map<int,int> map;
       int res=0,n=fruits.size();
       for(int i=0,j=0;i<n;i++){
            map[fruits[i]]++;
            while(map.size()>2){
                map[fruits[j]]--;
                if(map[fruits[j]]==0)map.erase(fruits[j]);
                j++;
            }
            res=max(res,i-j+1);
       }
       return res;
    }

类似题目:LCP 68. 美观的花束 - 力扣(LeetCode) 

思路:定义一个统计个数的数组或者哈希表都可以,然后用右边界去统计个数,左边界去满足题目要求,然后while条件去处理部满足情况的条件,并对其计数--,遍历下一个直到满足情况。

const int mod=1e9+7;
    int beautifulBouquet(vector<int>& flowers, int cnt) {
        unordered_map<int,int> dic;
        int n=flowers.size();
        long long res=0;
        for(int left=0,right=0;i<n;i++){
            dic[flowers[right]]++;
            while(dic[flowers[right]]>cnt) dic[flowers[left++]]--;
            res+=i-j+1;
        }
        return res%mod;
    }

提升题目:覆盖最小子串。76. 最小覆盖子串 - 力扣(LeetCode)

思路:定义两个数组,一个函数去判断是否覆盖住,主逻辑里面的函数主要就是,比对长度更新区间。

class Solution {
public:
    bool is_coverd(int cnt_s[], int cnt_t[]){
        for(int i='A';i<'Z';i++){
            if(cnt_s[i]<cnt_t[i])return false;
        }
        for(int i='a';i<'z';i++){
            if(cnt_s[i]<cnt_t[i])return false;
        }
        return true;
    }
    string minWindow(string s, string t) {
        int n=s.length();
        int ans_left=-1;int ans_right=n;
        int cnt_s[128]{};
        int cnt_t[128]{};
        for(char c:t)cnt_t[c]++;
        for(int left=0,right=0;right<n;right++){
            cnt_s[s[right]]++;
            while(is_coverd(cnt_s,cnt_t)){
                if(right-left<ans_right-ans_left){
                    ans_left=left,ans_right=right;
                }
                cnt_s[s[left++]]--;
            }
        }
        return ans_left<0?"":s.substr(ans_left,ans_right-ans_left+1);
    }
};

二、螺旋数组

 思路:主要是先定义一个N^2的二维空间,然后初始化变量,主要是对边界条件的限制,必须明确,要不然会导致待出报错。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>>res(n,vector<int>(n,0));
        int startx=0,starty=0;
        int loop=n/2;//循环走四次
        int mid=n/2;//中心位置
        int count=1;//给空格赋值
        int offset=1;//控制遍历长度
        int i,j;
        while(loop--){
            i=startx;j=starty;
            for(j;j<n-offset;j++) res[i][j]=count++;
            for(i;i<n-offset;i++) res[i][j]=count++;
            for(;j>starty;j--)    res[i][j]=count++;
            for(;i>startx;i--)    res[i][j]=count++;
            startx++;starty++;offset+=1;
        } 
        if(n%2) res[mid][mid]=count;
        return res;
    }
};

三、区间和

 思路:开大小和n对应的空间,把之前计算的中间数据累加起来,然后求区间和的时候直接调用辅助空间的数据,进行计算,这样能避免大数据的时间复杂度过高。

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

上面那个是一般写法,计算更加复杂的场景的时候会超时。

#include<iostream>
#include<vector>
using namespace std;

int main(){
    int a,b,n;
    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=0;
        if(a==0)sum=p[b];
        else sum=p[b]-p[a-1];
        cout<<sum<<endl;
    }
    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];
        }
    }// 统计横向
    vector<int> x(n, 0);
    for (int i = 0; i < n; i++) {
        for (int j = 0 ; j < m; j++) {
            x[i] += vec[i][j];
        }
    }// 统计纵向
    vector<int> y(m , 0);
    for (int j = 0; j < m; j++) {
        for (int i = 0 ; i < n; i++) {
            y[j] += vec[i][j];
        }
    }
    int result = INT_MAX;
    int xCut = 0;
    for (int i = 0 ; i < n; i++) {
        xCut += x[i];
        result = min(result, abs(sum - xCut - xCut));
    }
    int yCut = 0;
    for (int j = 0; j < m; j++) {
        yCut += y[j];
        result = min(result, abs(sum - yCut - yCut));
    }
    cout << result << endl;
}

总结

在数组的题型中,我们常使用的方法有二分提升查找的效率双指针提升遍历的效率滑动窗口提升对一段数据的处理能力前缀和用空间换时间的效率、同时需要注意边界条件的控制,比如螺旋数组一不小心就会越界,报错,基本熟悉数组的操作,我们对于复杂的应用场景题目处理才能提升!!!共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值