代码随想录算法训练营第二天|数组双指针&滑动窗口&模拟

双指针

977.有序数组的平方

在这里插入图片描述
可用数组中绝对值的最小值和其左边一个索引,为两个指针的起始点进行遍历(平方值从小到大)

class Solution {
public:
    int abs(int a){
        if(a >= 0) return a;
        else return -a;
    }
    vector<int> sortedSquares(vector<int>& nums) {
        int l=0,r=0;
        while(r<nums.size() && nums[r]<0) r++;
        l = r - 1;
        vector<int> res;
        while(l>=0 && r<nums.size()){
            if(abs(nums[l])<=nums[r]){
                res.push_back(nums[l]*nums[l]);
                l--;
            }
            else{
                res.push_back(nums[r]*nums[r]);
                r++;
            }
        }
        while(l>=0){
            res.push_back(nums[l]*nums[l]);
            l--;
        }
        while(r<nums.size()){
            res.push_back(nums[r]*nums[r]);
            r++;
        }
        return res;
    }
};

也可以从数组的左右端点进行遍历,这两个数的平方值中必有一个为最大值,这样依次遍历到的是平方值递减的数组,所以我们对于创建的数组res从后往前存入,这样就实现了res的递增。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        // 创建一个n个元素且全为0的数组
        vector<int> res(n, 0);
        int k = n;
        int i = 0, j = n - 1;
        while(i<=j){
            if(nums[i]*nums[i] <= nums[j]*nums[j]){
                res[--k] = nums[j]*nums[j];
                j--;
            }
            else{
                res[--k] = nums[i]*nums[i];
                i++;
            }
        }
        return res;
    }
};

滑动窗口

209.长度最小的子数组

在这里插入图片描述
暴力法
枚举每个左端点,然后继续枚举右端点

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        int minlen = n + 1;
        for(int i = 0; i < n; i++){
            if(nums[i] >= target) return 1;
            int sum = nums[i];
            int j = i + 1;
            while(j < n && sum < target){
                sum += nums[j];
                j++;
            }
            if(sum >= target)
                minlen = min(minlen, j - i); 
        }
        if(minlen == n + 1) return 0;
        else return minlen;
    }
};

只能过18个点

滑动窗口
滑动窗口与暴力法的不同在于它的循环只需要遍历一次,并且它遍历的不是左端点,而是右端点。
实现滑动窗口,主要确定如下三点:

  1. 窗口内代表了什么
    窗口内代表满足条件的区间
  2. 窗口的左端点什么时候开始移动?
    当满足条件时,左端点开始移动,直到不满足条件位置,这个过程中可以进行记录的操作
  3. 右端点什么时候开始移动?
    循环每次移动的就是右端点

根据上述条件,我们代入到题目中
窗口内代表满足区间和>=target的区间

左端点再区间和>=target的时候开始移动,直到不满足条件为止

右端点每次循环即会移动一次

模板:

  1. 首先外循环遍历右端点
  2. 在循环内设置变量
  3. 再次用循环判断区间[l,r]是否满足条件,如果满足条件则l向右移动,直到不满足条件为止(或者从不满足条件到满足条件为止),这期间可以持续记录某些值的 操作
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        int minlen = n + 1;
        int l = 0, r = 0;
        int sum = 0;
        while(r < n){
            sum += nums[r];
            while(l <= r && sum >= target){
                int sublen = r - l + 1;
                minlen = min(minlen, sublen);
                sum-=nums[l++];
            }
            r++;
        }
        if(minlen == n + 1) return 0;
        return minlen;
    }
};

904.水果成蓝

在这里插入图片描述
套模板

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        map<int,int> mp;
        int i = 0, j = 0;
        int n = fruits.size();
        int value;
        int maxsize = 0;
        // 1.首先外循环遍历右指针
        while(j < n){
            // 进行赋值操作,把当前水果的数量放入map里
            value = fruits[j];
            mp[value] += 1;
            // 内循环遍历左指针,直到不满足条件为止
            while(mp.size() > 2){
                value = fruits[i];
                mp[value]--;
                // 如果没有水果了就把它从map里删除
                if(mp[value] == 0) mp.erase(value);
                i++;
            }
            // 在循环之后,区间是已经是满足条件了(水果种类<=2),这时候开始记录值
            int sum = 0;
            for (const auto& pair : mp){
                sum += pair.second;
            }
            maxsize = max(maxsize,sum);
            // 右指针移动
            j++;
        }
        return maxsize;
    }
};

76.最小覆盖字串

在这里插入图片描述

class Solution {
public:
    bool check(unordered_map<char,int>& a,unordered_map<char,int>& b){
        // 判断b是否是a的子集
        for(const auto& pair: b){
            if(pair.second > a[pair.first]) return false;
        }
        return true;
    }
    string minWindow(string s, string t) {
        int i = 0, j = 0;
        int n = s.length();
        int minlen = n + 1;
        unordered_map<char,int> a,b;
        int l = -1;
        for(i = 0; i < t.length(); i++){
            b[t[i]] += 1;
        }
        i = 0;
        // 首先右端点进行循环
        while(j < n){
            // 进行赋值操作
            char c = s[j];
            a[c] += 1;
            // 左端点进行循环移动直到区间不满足条件为止,期间一直记录长度
            while(check(a,b) && i<=j){
                // 满足条件的同时一直记录最小长度和左边界
                int len = j - i + 1;
                if(len<minlen){
                    minlen = len;
                    l = i;
                }
                // 左端点移动
                a[s[i]] -= 1;
                i++;
            }
            // 右端点移动
            j++;
        }
        if(l == -1) return "";
        else return s.substr(l,minlen);
    }
};

模拟

59.螺旋矩阵

在这里插入图片描述
注意边界条件,固定为左闭右开,统一化
l和r是用来遍历的指针,rb等为边界,遍历的圈数为loop
n为奇数或偶数时中心点是否需要赋值也需要判断
每一圈遍历之后边界对应缩小或放大
l和r会回到该圈的起始位置,所以再一圈遍历完后需要让l和r都+1

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> a(n, vector<int>(n, 0));
        int t = 0;
        int l = 0, r = 0;
        int rb = n - 1, bb = n - 1, lb = 0, tb = 0;
        int target = n * n;
        // 转的圈数
        int loop = n / 2;
        while(loop--){
            while(l < rb && t<= target){
                a[r][l++] = ++t;
            }
            rb--;
            while(r < bb && t<= target){
                a[r++][l] = ++t;
            }
            bb--;
            while(l > lb && t<= target){
                a[r][l--] = ++t;
            }
            lb++;
            while(r > tb && t<= target){
                a[r--][l] = ++t;
            }
            tb++;
            // l和r最终会回到起始位置,下一圈的位置需要+1
            l++,r++;
        }
        // 如果n为奇数,那么需要赋值中心点
        if(n %2 !=0) a[n/2][n/2] = ++t;
        return a;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值