数据结构-线性表(数组)-双指针-不定长滑动窗口567&713&3&697

什么时候使用不定长滑动窗口(尺取法)?

寻找一个容器中是否有(有多少个)子容器符合某种条件,根据上述,这个条件大概率是指:小于某某值,最长不重复。可以使用尺取法。

尺取法做法

尺取法做法: 现在遇到的尺取法就是固定右边界,这时该窗口刚好符合不超过条件,当右边界往右再移动一下就会不符合条件。这时就需要右移左边界。左右指针都只会增加不会减小。

跟前缀和法的区别?

什么时候尺取法?什么时候前缀和法?那种场景下使用呢?

leetcode题

713. 乘积小于 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

  1. 用什么方法?为啥用这个方法?

根据题目要求关键点在于 找到子数组小于k。子数组 && 小于某某值,考虑尺取法。当从头开始,右边界一直右移,窗口积当>=k时,若右边界还是右移,则积只会越来越大,所以此时固定右边界,右移左边界,直到积<k(符合条件)。当窗口积<k时,则窗口中的子数组积都是<k。
计算每个右边界对应符合条件的子数组数量:为什么是res=right-left+1而不是res=1+2+…+(right-left+1)?因为这是计算的只包含新添加进来的右边界元素的子数组,避免了与前面的重合。 例如第二次循环时 10*5<k 此时以及记录过[10] 该窗口子数组只需记录[5] [10,5]即可,也就是+2,而后者是+3,这就重复记录了[10]。

  1. 代码
class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        int len = nums.size();
        int res = 0;
        int left = 0,right = 0;
        int product = 1;
        for(right;right<len;++right){
            product *= nums[right];
            while(left<=right && product>k){
                product /= nums[left];
                ++left;
            }
            res += right-left+1;//就算知道这个方法也想不到的地方
        }
        return res;
    }
};
  1. 学到了什么?
    尺取法求窗口中的子数组的个数
    尺取法的思路做法

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

  • 用什么方法?为啥用这个方法?
    看到题目知道是找子串,可以考虑滑动窗口。
  1. 右指针一直往前走(窗口增长),直到添加右指针指向的元素后不符合条件(本题是有重复)
  2. 当右指针走到不满足条件(本题是有重复)时候需要调整左值针(也就是缩短窗口),直到再次满足条件(子串中无重复字符)
  3. 本题使用hash表来记录字符是否有重复。
    评论中看到的方法:
    每次添加一个,查看新添加元素是否与之前的字符相同,并记录长度。当相同时就标记与新添加的元素相同元素前的位置flag,下次新添加进来的元素与之前元素进行比较时就比较到flag就行,不必一直比较到0。每次新添加进来 进行比较时,len初始都为一。
  • 代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.size();
        if(len == 0) return 0;
        int res = 0;
        unordered_map<char,int> hashmap;
        int left = 0;
        for(int right = 0;right<len;right++){
            ++hashmap[s[right]];
            while(hashmap[s[right]]>1){
                --hashmap[s[left]];
                ++left;
            }
            if(right-left+1>res) res = right-left+1;
        }
        return res;
    }
};
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s.size() == 0) return 0;
        int maxlen = 1,flag = 0;
        for(int i = 1;i<s.size();++i){
            int len = 1;
            for(int j = i;j>flag;j--){
            	//这里非常重要,保证了flag指向与新添加字符相同字符前一个位置。
                if(s[i]!=s[j-1]){
                    len++;
                }else{
                    flag = j;
                    break;
                }
            }
            maxlen = max(len,maxlen);
        }
        return maxlen;
    }
};
  • 学到了什么?
  1. 对于寻找不重复子容器的处理不仅仅可以用滑动窗口进行调整,还可以使用新添加一个元素,让该元素与现在的子容器中元素进行对比来判断是否有重复的元素。

697. 数组的度

  • 用什么方法?为啥用这个方法?
    我能想到用哈希表记录下数组中每个元素的度,然后找到最大度对应的元素,并且能想到最短子数组两端一定是该元素。但是没有想到怎么解决:因为符合条件的元素可能有多个,即多个不同的数在原数组中出现次数相同
    看解答后可以一个元素在和哈希表中对应一个保存三个元素数组(出现次数,第一次出现的下标,最后一次出现的下标)这样就解决了我解决不了的。
  • 代码
    我自己写的 通过87/89
class Solution {
public:
    int findShortestSubArray(vector<int>& nums) {
        int len = nums.size();
        int degree = 0;
        int num;
        int left = 0,right=len-1;
        unordered_map<int,int> hashmap;
        for(int i = 0;i<len;++i){
            ++hashmap[nums[i]];
            if(hashmap[nums[i]]>degree){
                degree = hashmap[nums[i]];
                num = nums[i];
            }
        }
        while(nums[left]!=num) left++;
        while(nums[right]!=num) right--;

        return right-left+1;
    }
};

看别人的代码,可以不使用数组记录第一次出现和最后一次出现,就能判断出子数组的长度

class Solution {
public:
    int findShortestSubArray(vector<int>& nums) {
        int len = nums.size();
        int degree = 0,res = 50000;
        int hasmap[50000] = {0};
        for(int i = 0;i<len;++i){//统计整个数组的度为多大,并记录每个元素出现的次数
            ++hasmap[nums[i]];
            if(hasmap[nums[i]]>degree) degree = hasmap[nums[i]];
        }
        if(degree==1) return 1;
        int current_degree = 0;
        int current_maxlen = 0;
        for(int i = 0;i<len;++i){
            if(hasmap[nums[i]]==degree){//只处理出现次数与度相等的元素
            //当current_degree减为1时,j就指向nums[i]最后一次出现的位置 这样就解决了我的算法里解决不了的问题,可以判断子数组的长度了。
                current_degree = degree;
                for(int j = i+1;j<len;++j){
                    if(nums[j]==nums[i]) current_degree--;
                    if(current_degree==1){
                        current_maxlen = j-i+1;
                        res = min(current_maxlen,res);
                        hasmap[nums[i]] = 0;//该元素以及判断完成,需要清除,避免重复判断。
                        break;
                    }   
                }
            }  
        }
        return res;
    }
};

官方题解代码

class Solution {
public:
    int findShortestSubArray(vector<int>& nums) {
        int len = nums.size();
        int degree = 0,minlen = 50000;
        unordered_map<int,vector<int>> hashmap;
        for(int i = 0;i<len;++i){
            if(hashmap.count(nums[i])){
                hashmap[nums[i]][0]++;
                hashmap[nums[i]][2] = i;
            }else{
                hashmap[nums[i]] = {1,i,i};
            }
        }
        for(auto [_,vec]:hashmap){
            if(vec[0]>degree){
                degree = vec[0];
                minlen = vec[2]-vec[1]+1;
            }else if(vec[0]==degree){
                if(vec[2]-vec[1]+1<minlen) minlen = vec[2]-vec[1]+1;
            }
        }
        //对哈希表存储和遍历 重要C++知识点
        //for(auto [_,vec]:hashmap){
        //    if(vec[0]>=degree){
        //        degree = vec[0];
        //        if(vec[2]-vec[1]+1<minlen){
        //            minlen = vec[2]-vec[1]+1;
        //        }
        //    }
        //}
        return minlen;
    }
};
  • 学到了什么?
  1. 哈希表一个元素对应一个数组
  2. 哈希表存储顺序
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值