算法——滑动窗口

什么是窗口?就是符合题目要求的区域内的数据,将每次符合数据的窗口内的数据记录下来,然后将窗口后移,寻找其他符合要求的数据,每次进入窗口和退出窗口都需要一定的要求

一、长度最小的子数组

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

思路

代码
class Solution {
public:
    bool istarget(int x, int target)
    {
        if (x >= target)
            return true;
        return false;
    }
    int minSubArrayLen(int target, vector<int>& nums) {
        //先判断是否有总和是否大于target,如果不是那么就直接返回0
        int judgesum = 0;
        for (auto e : nums)
        {
            judgesum += e;
        }
        if (judgesum < target)
            return 0;
        int len = nums.size(), left = 0, right = 0;
        //开拓一个数组,放入符合条件的长度,之后进行遍历,进行排序,然后返回最小值
        vector<int> ans;
        int sum = 0;
        while (left < len && right < len)
        {
            sum += nums[right];
            if (istarget(sum,target))//如果超过了
            {
                while (sum>=target)
                {
                    ans.push_back(right - left + 1);
                    sum -= nums[left];
                    ++left;
                }
                ++right;
            }    
            else//不满足,那么右指针往后走 
            {
                ++right;
            }
        }
        sort(ans.begin(), ans.end());
        return ans[0];
    }
};

二、无重复字符的最长子串

LCR 016. 无重复字符的最长子串 - 力扣(LeetCode)

思路

照常还是使用滑动窗口,符合要求的就进入窗口并且记录,不符合要求的就出窗口

这道题的重点就是在于如何判断已经出现过的字母——需要用到哈希表

但是这里会有特殊情况

若是在中间地方出现了重复的,例如pwwke,那么我们前边的pw这两个字母都不要了,重新统计

代码
 int lengthOfLongestSubstring(string s) {
       unordered_map<char,int> mp;
       int l=0,r=0;
       int ans=0;
       while(r<s.size())
       {
           mp[s[r]]++;//右指针对应的下标的字母个数往上加
           while(mp[s[r]]==2)//如果对应的字母出现两次了,说明这时候就要更新开头的位置,并且要将其对应的字母的个数要重置,避免多余的删除
           {
               mp[s[l++]]--;
           }
           ans=max(ans,r-l+1);//每一次都要更新最长的长度
           r++;//右指针往后
       }
       return ans;
    }

三、最大连续1的个数Ⅲ

1004. 最大连续1的个数 III - 力扣(LeetCode)

思路

这个题目其实可以暴力枚举,但是枚举终究是会超时的,那我们需要用什么来减少时间复杂度?

——滑动窗口

此处的重点是在于将统计翻转后连续1的个数——>一个连续数组里0的个数

然后,我们要注意

当0的个数=k的时候,不是立刻停止读取,而是要判断0下一个数字是否为0,若为0,那就停止right的移动。

若为1,那么right往后的话,连续数组里0的个数还是没有超过k,也就是翻转的次数没有超过k。

为了方便,我选择了当统计0的个数>3的时候,也就是读取到多余的一个0的时候,就开始移动窗口。

并且此题与上两题不同的是,这题是实时记录长度,其他两题是进入窗口后才进行数据的记录

代码
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int ret=0;
        for(int left=0,right=0,zero=0;right<nums.size();right++)
        {
            if(nums[right]==0)zero++;
            while(zero>k)
            {
               if(nums[left++]==0)
                    zero--; 
            }
            ret=max(ret,right-left+1);
        }
        return ret;
    }
};

四、将x减到0的最小操作数

1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)

思路

这道题还是按照滑动窗口,但是题目中给到“移除数组最左端或最右边的元素”。

左右遍历是可以的,但会使代码量及其复杂,我们是否可以换个思路——找到连续的数组使其符合什么关系?

所以我们的思路就转换为——找到连续的数组,使其总和为sum-x,并且找到其最长的长度

代码
int minOperations(vector<int>& nums, int x) {
        int len=nums.size(),sum=0;
        for(auto e:nums)
        {
            sum+=e;
        }
        int target=sum-x;
        int left=0,right=0,maxlen=-1,testsum=0;
        while(left<len&&right<len)
        {
            testsum+=nums[right];
            if(testsum==target)
            {
                maxlen=max(maxlen,right-left+1);
                ++right;
            }
            if(testsum>target)
            {
                while(testsum>target&&left<len)
                {
                     testsum-=nums[left++];
                }
                //去重后也需要进行一个判断
                if(testsum==target)
                {
                    maxlen=max(maxlen,right-left+1);
                    ++right;
                }
            }
            if(testsum<target)
            {
                ++right;
            }
        }
        if (maxlen == -1)//若还为-1,说明没有找到
            return -1;
        return (len-maxlen);//总长度减去连续的最长长度,便是两端最短的长度
    }

五、找到字符串中所有字母异位词

LCR 015. 找到字符串中所有字母异位词 - 力扣(LeetCode)

思路

这道题就是用滑动窗口,依次读取——但是要进行无序的比较,如何比较?——用哈希表。

用哈希表先记录要对照的字符串每个字母出现的次数,然后读取到窗口数——p的大小

然后进行判断

暴力枚举的思路

这样子是大致思路,是暴力枚举和滑动窗口的结合,但是判断会遇到两种情况

1.字符串里交替出现要检索的p,例如s="abab",p="ab",那么每次判断后都要清除前一个已经存在的哈希表的数据,并且将left指针向右移动

2.字符串里有连续的字符,但是中途出现了一个不属于p中的字符,那么这时候就要归位,要从不属于p这个字符后边位置开始。例如s="cbaeabcd",p="abc"

3.字符串里有连续的相同字母出现,此时那么就需要去重。例如s="bbcaacb",p="abc"。

优化

选用一个count来获取不重复有效字符的个数

这个例子不能反映count的优化程度,再举个例子

代码
 vector<int> findAnagrams(string s, string p) {
        vector<int> ans;
    int hashtest[26] = { 0 };
    int hashsearch[26] = { 0 };
    for (auto ch : p) hashtest[ch - 'a']++;//记录个数
    int m = p.size();
    for (int left = 0, right = 0, count = 0; right < s.size(); right++)
    {
        char in = s[right];
        if (++hashsearch[in - 'a'] <= hashtest[in - 'a'])++count;
        if (right - left + 1>m)//此时超出了窗口数,那么
        {
            char out = s[left++];
            if (hashsearch[out - 'a']-- <= hashtest[out - 'a']) count --;
        }
        if (count == m) ans.push_back(left);
    }
    return ans;
    }

六、串联所有单词的子串

30. 串联所有单词的子串 - 力扣(LeetCode)

思路

这道题和第五题中类似,只不过是从单词转变成为了字符串,所以我们只需要将判定条件变为,每经过一个子串的长度,那么就用哈希表进行判断,并且这个哈希表的键值对的键就是string类型。

同时,由于判断的长度总共为p中所有单词的长度,所以就是最多只能移动到words[0].size()*words.size()处

需要注意的是,我们需要用substr函数来读取字符串。

剩下的判定方式和有效个数的读取就是和第五题中单词的方式一样。

一旦满足的有效个数,那么就会进入去重的步骤。

代码

vector<int> findSubstring(string s, vector<string>& words) {
    unordered_map<string, int> hash1;
    vector<int> ans;
    for (auto& s : words)hash1[s]++;//将每一个字符串保存起来
    //我靠,用字符串比较
    int len = words[0].size(), m = words.size();
    for (int i = 0; i < len; i++)//每一段len就记录长度
    {
        unordered_map<string, int> hash2;
        for (int left = i, right=i, count = 0; right+len <= s.size(); right+=len)//count记录的是有效字符串的长度
        {
            string in = s.substr(right, len);
            hash2[in]++;
            if (hash2[in] <= hash1[in]) count++;//如果出现一次以下,count才会++
            if (right - left + 1 > len*m)//说明此时符合全部字符串的长度
            {
                string out = s.substr(left, len);//当做一个整体表明是要出窗口的
                if (hash2[out]-- <= hash1[out]) --count;
                left += len;//那么此时就left往后一个字符的长度
            }
            if (count == m)//有效字符的个数等于字符个数,那么就往答案里边存放数据
            {
                ans.push_back(left);
            }
        }
    }
    return ans;
}

七、水果成篮

904. 水果成篮 - 力扣(LeetCode)

思路

1.双指针

题目的要求就是获取两种类型的最大长度,我们可以定义两个变量firsttype和secondtype,分别代表着第一个树的类型,第二个树的类型。当读取到第三种类型的树时,那么就记录下长度,然后进行去重,并且从一开始读取到的第二种树的类型开始进行重新的判断。

代码
int totalFruit(vector<int>& fruits) {
    int len = fruits.size();
    if(len==1)
       return 1;
    if(len==2)
       return 2;
    vector<int> ans;
    int left = 0, right = 1;
    int firsttype = fruits[0], secondtype = -1;//记录两个种类对应的数字,而且还可以用来存放不同种类出现的位置
    int firsttmp = 0, secondtmp = 0,flag=0,transtime=0;
    while (right < len)
    {
        //mp[fruits[right]]++;
        if ((!flag)&&fruits[right] != firsttype)//开始判断第二次出现的种类
        {
            flag = 1;//表明出现了第二个数据
            secondtmp = right;//记录位置
            secondtype = fruits[right];//记录对应的数字
            transtime++;
        }
        if (flag)//flag=1表明已经有两个种类了
        {
            if (fruits[right] != secondtype && fruits[right] != firsttype)//出现第三种类型
            {
                ans.push_back(right - left);//直接放入数据,并且更新位置
                firsttype = secondtype;
                firsttmp = secondtmp;//直接更新第二个种类的位置
                left = firsttmp;
                flag = 0;
                right=firsttmp;
                continue;
                //这个判断的循环,right不能计入
            }
            if (right == len-1)//right已经到边界都没有出现第三个数字
            {
                ans.push_back(right - left+1);
                ++right;
                continue;
            }
           
        }
        ++right;
    }
    if (transtime == 0)//没有第二类树了,那么就直接返回数组的长度
    {
        return fruits.size();
    }
    sort(ans.begin(), ans.end());
    return ans[ans.size() - 1];
}

2.滑动窗口

要知道哈希表不仅能保存每种类型出现的个数,还能进行出现的类型的个数的统计。

所以我们可以通过统计hash表中的长度,来判断是否出现了第三种树,出现了第三种树后,那么就直接进行类型的去重,并且可以实时观察哪个种类优先被去重到0——也就是当前连续的树种类不多于2。

此处用for循环来判断,这样子方便右指针right的移动

代码
int totalFruit(vector<int>& fruits) {
    unordered_map<int,int> hash;
    int ret=0;
    for(int left=0,right=0;right<fruits.size();right++)
    {
        hash[fruits[right]]++;
        while(hash.size()>2)//表示种类大于2
        {
            hash[fruits[left]]--;
            if(hash[fruits[left]]==0)
                hash.erase(fruits[left]);
            left++;
        }
        ret=max(ret,right-left+1);
    }
    return ret;
}

八、最小覆盖子串

LCR 017. 最小覆盖子串 - 力扣(LeetCode)

思路

本题思路跟第五题中找字母异位词类似,这不过第五题中要找的是连续的,此题找的不是连续的,所以我们仅需要一个哈希表来记录,并且同样用count来记录有效字符个数,当有效字符个数大于要查找的字符数时,那么就不进行有效字符个数的记录。

而且此处不需要对于多余的字母进行去重,仅需要对于符合有效字符个数的时候进行记录

代码

string minWindow(string s, string t) {
	unordered_map<char, int> hashtest,hashsearch;
	for (auto& e : t)
	{
		hashtest[e]++;//记录下要查找的t的字符的种类
	}
	string ret = "";
	int left = 0, right = 0, count = 0;
	for (; right < s.size(); right++)
	{
		hashsearch[s[right]]++;
		if (hashsearch[s[right]] <= hashtest[s[right]]) count++;//若超出这个字符限定的次数,那么就不会计入有效字符个数
		while (hashsearch[s[left]] > hashtest[s[left]])//去重
		{
			--hashsearch[s[left++]];
		}
		if (count == t.size())
			if (ret.empty() || right - left + 1 < ret.size()) ret = s.substr(left, right - left + 1);
	}
	return ret;
}

总结

滑动窗口这部分的难点主要是在于:进窗口条件+出窗口条件+进窗口时对于变量的处理+出窗口时对于变量的处理+对于数据的记录。

弄清楚这些条件后,便可以轻松解出题目。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在处理图像滑动窗口算法时,可以使用Python来实现。首先,你需要将图像切片编号并存储为数字标识的文件名,比如1.png、2.png等。然后,你可以使用滑动窗口方法来获取所有卡顿的小区间的起始位置。 在Python中,你可以使用numpy库来进行滑动窗口的实现。下面是一个实例代码: ```python import numpy as np # 图像切片编号列表 image_slices = [1, 2, 3, 4, 5, 6, 7, 8, 9, ...] # 滑动窗口大小和步长 window_size = 3 step_size = 1 # 存储所有窗口的起始位置 window_positions = [] for i in range(len(image_slices) - window_size + 1): window_positions.append(image_slices[i: i + window_size]) print("所有窗口的起始位置:", window_positions) ``` 这段代码首先定义了图像切片编号列表`image_slices`,然后指定了滑动窗口的大小`window_size`和步长`step_size`。接着使用一个循环遍历整个图像切片编号列表,每次取出连续的窗口大小的切片,并将其存储到`window_positions`列表中。最后,输出所有窗口的起始位置。 请注意,这只是一个简单的示例,实际应用中你可能需要根据具体情况进行适当的调整和扩展。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [【python】滑动窗口算法](https://blog.csdn.net/darlingmz/article/details/125815782)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Python在大数据方面的应用前景](https://download.csdn.net/download/milk416666/88264587)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [数学模型——python实现滑动窗口算法(特征匹配)](https://blog.csdn.net/qq_55433334/article/details/127179881)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nick-An

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值