“滑动窗口”思想在算法里面的应用

news/2024/9/23 11:14:47 标签: 算法, 数据结构, c++, leetcode, visual studio, 笔记

目录

一· 无重复字符串的最长子串

链接:无重复字符的最长子串

 1. 题目分析

解法一:暴力求解

借助2个“指针”:left , right 指针,依次固定left  指针,让right指针进行遍历,每遇到一个最大的

字符串进行更新即可。

对应时间复杂度:O(N^2)

解法二:滑动窗口

2个指针移动方向一致,对right 指针所指向的元素进行遍历,对left 指针指向元素进行删除

此时借助一个临时数组判断是否有元素的重写。

2. 算法原理

暴力求解的草图:

模拟暴力求解的过程:

我们发现当出现重复元素的时候,每次right 指针重新回到新的left 所指向的

位置,在一次进行遍历的时候,求解最大长度的时候 只有2中可能的结果:要不就是等于初次的最

大长度;要不就是小于初次的最大长度

因为在[ left ,right ] 这个区间内left ++ 一次,都会让 [ left ,right ] 这个区间内l 元素的个数减少一

个。

因此我们可以进行优化当遇到重复元素的时候,让right 指针不动,移动left 指针。

这正是“滑动窗口 ” 的应用。

时间复杂度:O(N)

此时我们需要借助一个临时的数组,来判断当前的元素是否重复。

出窗口结束条件:right 所指向的元素没有出现过就可以 ,此时删除left  所指向的没有元素。

3.  OJ代码
class Solution {
public:
    int lengthOfLongestSubstring(string s)
     {
        //滑动窗口
        int left = 0,right = 0,n = s.size();
        int tmp[128] = {0};
        int len = 0;
        while(right < n)
        {
            //进窗口
          
            tmp[ s[right]] ++;
          
            //出窗口
            while( tmp[ s[right]] > 1)
            {
               
                tmp[ s[left]]--;//tmp 当前位置元素为0
                left++;
            }
               //更新
               len = max (right-left+1,len);
            right++;
        }
        return len;

    }
};

二· 最大连续1的个数

链接:最大连续1的个数

 

 1. 题目分析

常规思路就是:遍历数组,遇到数字0 就改写成数字1,并记录一下Count 的大小当Count == 0的

个数的时候,就是这个区间里面1的个数。

草图:

换个思路就是:要求返回1所在区间里面 0 的个数<= k 的大小,之后便是找子区间里面1的个数最

多 的问题 

解法一:暴力求解

 固定left  = 0,right = 0 ,count = 0

count 统计子数组里面0 的个数

解法二:滑动窗口

 每一轮结束后找到指定区间的时候,right 指针都需要在会去left  指向下一个元素的位置,但是新

一轮区间里面1的个数是小于或者等于上一轮[ left, right ] 指向区间里面1的个数,因为此时指向的

区间只是相较于上一轮指向区间减少1个元素,所以针对这个情况,可以使用“滑动窗口”的思想

2. 算法原理

分为4小步:

1)“进窗口”

nums[right++] == 0

count ++;

2)判断

if(count > k)

3)“出窗口”

if(nums[left] == 0 )

  count--;

4)结果更新

len = max(len,right -left);

3.  OJ代码
3.1 解法一代码
int longestOnes(vector<int>& nums, int k)
{
	int left = 0, right = 0, count = 0;//统计0 出现的次数
	int len = 0;
	for (; left < nums.size(); ++left)
	{
		right = left;
		for (; right < nums.size(); ++right)
		{
			if (nums[right] == 0)
				count++;
			//结果更新
			if (count > k)
			{
				len = max(len, right - left );
				count = 0;//重新归为0
				break;
			}
		}
	
	}
	return len;
}
3.2 解法二代码
  int longestOnes(vector<int>& nums, int k) 
    {
        int left = 0, right = 0, count = 0;//统计0 出现的次数
		int len = 0;
		while (right < nums.size())
		{
			if (nums[right] == 0)
				count++;
			right++;
			// if (count > k)
			// {
			// 	if (nums[left++] == 0)
			// 		count--;
			// }
            //对于这个代码可以进优化,可能存在非连续的0
           while(count > k)
			{
				if (nums[left++] == 0)
					count--;
			}
			len = max(len, right - left);
	
			
		}
		return len;

    }

三· 将x 减小到0的最小操作数

链接:将x 减小到0的最小操作数

 

 1. 题目分析

拿到此题目的时候,初始想法都是,遍历此数组,进行加和直至遍历所得的总和为 x 时候,返回此

时遍历元素的个数,即为题目所求。

图解分析:

2. 算法原理

其实换个思路就是:

找出一个最长的子数组的,同时这个子数组的所有元素之和恰好等于数组元素总和减去 x 

len: 最长子数组的长度

add: 最长子数组的所有元素之和 

sum : 数组所有元素之和

最小操作数:n - len 

n : 数组的原始大小

1)解法一:暴力求解

“双指针”思想:固定left = 0,right = 0,add = 0,len  = 0(记录最长子数组的元素个数)

对应时间 复杂度:O(N^2)

2)解法二:滑动窗口

还是一样的分析:每当add >= sum -x 的时候,right 都需要再次重新回到 left 所在元素的下一个位

置,再重新执行一样的操作,其实这个过程是没有必要的,此时只需要删除left 所指向的元素就

行,直至add <= sum -x.

3.  OJ代码

暴力求解代码:

int minOperations(vector<int>& nums, int x)
{
	//找出自由光最长子数组同时需要满足所有元素之和恰好为 sum - x
	int n = nums.size();
	if (x < nums[0] || x < nums[n - 1])
		return -1;//不存在
	int left = 0, right = 0, add = 0, sum = 0, len = 0;
	for (auto it : nums)
	{
		sum += it;
	}
    if(x > sum)
      return -1;
	for (; left < n; ++left)
	{
		for (right = left; right < n; right++)
		{
			add += nums[right];
			if (add > sum - x)
			{
				break;
			}
		  if (add == sum - x)
			{
				len = max(len, right - left + 1);
				break;
			}
			
		}
		add = 0;
	}
	return n - len;
}

滑动窗口代码:

int minOperations(vector<int>& nums, int x)
{
	//滑动窗口
	int n = nums.size();

	int sum = 0, add = 0, len = 0;
	for (auto it : nums)
		sum += it;
	if (x > sum)
		return -1;
	int left = 0, right = 0;
	while (right < n)
	{
		add += nums[right++];
		while (add > sum - x)
		{
			add -= nums[left++];
		}
		if (add == sum - x)
			len = max(len, right - left);
	}
	return len == 0 ? -1:n - len;
}

四.  找到字符串里面所有字母异位词

链接:找到字符串中所有字母异位词

 

1. 题目分析

常规思路:就是对s 这个字符串依次进行遍历,同时统计每一个有效字符出现的次数

对应的时间复杂度:O(N^2)

2. 算法原理

咱这里直接上“滑动窗口”的思想:

固定2个指针left = 0,right = 0;

定义2个数组 hash1[26] :用来存储p 这个字符串中每一个字符出现的次数

hash2[26]: 用来存储 s 这个字符串里面每隔 len  个字符里面有效字符出现的次数

len : p 这个字符串的大小

同时借助一个变量 flag :默认2数组里面的元素相同(便于后期在进行检查的时候,好判断)

 之所以把2个数组的大小固定为26:是因为题目中已经说明了:

3.OJ代码
vector<int> findAnagrams(string& s, string& p)
{
	//滑动窗口 + 2个数组(统计每一个字符出现的次数)
	int hash1[26] = { 0 };//统计p里面每一个字符出现的个数
	int hash2[26] = { 0 };//统计s里面每len个字符里面 每一个字符出现的个数
	int len = p.size();
	//统计p 里面每一个字符的个数
	int i = 0;
	while (i < len)
	{
		hash1[p[i] - 'a'] ++;//映射的思想
		i++;
	}

	int flag = 1;//标志位:默认2个字符数组相同

	int left = 0;
	int right = 0;
	vector<int> ret;
	while (right < s.size())
	{
		//进窗口
		hash2[s[right] - 'a']++;
		//判断&出窗口
		if ((right - left + 1) > len)
		{
			//窗口大小是固定的,所以只需要删除一个元素就可以
			hash2[s[left] - 'a']--;
			left++;
		}
		//更新结果
		if ((right - left + 1) == len)
		{
			int j = 0;
			i = 0;
			flag = 1;
			//while (n--)//注意不能判断len 次,是对全部数组进行判断
			while (i < 26)
			{
				if (hash1[i++] != hash2[j++])
				{
					flag = 0;
					break;
				}

			}
			if (flag == 1)
			{
				ret.push_back(left);
			}
		}
		right++;

	}
    // 出了循环,还需要在进判断依次是否,满足一个有效的区间
	if ((right - left + 1) == len)
	{
		int j = 0;
		i = 0;
		flag = 1;
		//while (n--)//注意不能判断len 次,是对全部数组进行判断
		while (i < 26)
		{
			if (hash1[i++] != hash2[j++])
			{
				flag = 0;
				break;
			}

		}
		if (flag == 1)
		{
			ret.push_back(left);
		}
	}
	return ret;
}

 其实对于这个代码还是有优化的空间的。

此代码的时间复杂度是:O(N),准确的说是O(26N)

在进行结果更新的时候,可以采用一个Count 计数的方法

Count:记录子数组里面的有效字符的个数,这样就可以减少判断的次数。

需要在进窗口,出窗口,以及结果更新的时候,都需要对Count 进行维护。

注意Count 含义: 记录的是有效字符的个数

进窗口:

判断当前 right 所指向的元素在 hash2这个数组里面的指定位置出现的次数是否 <= hash1

这个数组里面指定位置出现的次数。若是满足count++

出窗口:

对left 指向的元素进行删除,注意Count-- 的条件

只有当 left 指向的字符出现的次数  <=  在p 这个字符串里面出现的次数,才可以Count --

结果更新:

当Count == len 的时候,left  所指向的元素就是一个有效的位置。

vector<int> findAnagrams(string& s, string& p)
{
	//采用Count记录有效字符出现的个数
	int count = 0;
	int hash1[26] = { 0 };
	int hash2[26] = { 0 };
	int len = p.size();
	//统计p 字符串里面每一个字符出现 次数
	for (auto it : p)
		hash1[it - 'a']++;
	int right = 0, left = 0;
	vector<int>ret;
	while (right < s.size())
	{
		//进窗口
		char in = s[right];
		if (hash2[in - 'a']++ < hash1[in - 'a'])
			count++;
		//出窗口
		if (right - left+1 > len)
		{
			char out = s[left++];
			if (hash2[out - 'a']-- <= hash1[out - 'a'])
				count--;

		}
		//结果更新
		if (count == len)
			ret.push_back(left);
		right++;
	}
	return ret;
}

结语:

以上就是关于“滑动窗口”部分应用的OJ题目,对于这一思想的掌握,需要我们在暴力算法思想的基础之上进行优化,所以“暴力求解”是我们进入“滑动窗口”世界之门的一把“钥匙”,对于滑动窗

口的掌握,无非就是分4个小步骤:

1)进窗口

2)判断

3)出窗口

4)结果更新

可能结果更新这一步骤,不一定就是在“出窗口”之后,也可能在前面或者同时进行。总之,刷算法

题,画图结合思想是必不可少的,希望各位看到此篇博客,对于“滑动窗口”这一思想的理解和掌

握,可以有所帮助。


http://www.niftyadmin.cn/n/5671842.html

相关文章

css 样式简单学习(一)

目录 1. css 介绍 1.1 css 样式 1.2 css代码风格 1.2.1 书写格式 1.2.2 样式大小写​编辑 1.2.3 空格规范 2. 基础选择器 2.1 选择器的作用​编辑 2.2 选择器的分类 2.3 基础选择器 2.3.1 标签选择器​编辑 2.3.2 类选择器​编辑 2.3.3 类选择器-多类名​编辑 2.…

FLStudio21Mac版flstudio v21.2.1.3430简体中文版下载(含Win/Mac)

给大家介绍了许多FL21版本&#xff0c;今天给大家介绍一款FL Studio21Mac版本&#xff0c;如果是Mac电脑的朋友请千万不要错过&#xff0c;当然我也不会忽略掉Win系统的FL&#xff0c;链接我会放在文章&#xff0c;供大家下载与分享&#xff0c;如果有其他问题&#xff0c;欢迎…

【C语言零基础入门篇 - 16】:栈和队列

文章目录 栈和队列栈栈功能的实现源代码 队列队列功能的实现源代码 栈和队列 栈 什么是栈&#xff1a;功能受限的线性数据结构 栈的特点&#xff1a;先进后出 。例如&#xff1a;仓库进货、出货。 栈只有一个开口&#xff0c;先进去的数据在栈底&#xff08;bottom&#xf…

斗破C++编程入门系列之三十二:继承与派生:派生类的构造函数(一星斗师)

斗破C目录&#xff1a; 斗破C编程入门系列之前言&#xff08;斗之气三段&#xff09; 斗破C编程入门系列之二&#xff1a;Qt的使用介绍&#xff08;斗之气三段&#xff09; 斗破C编程入门系列之三&#xff1a;数据结构&#xff08;斗之气三段&#xff09; 斗破C编程入门系列之…

2024自学手册——网络安全(黑客技术)

前言 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 如何成为一名黑客 很多朋友在学习安全方面都会半路转行&#xff0c…

网安新声 | 黎巴嫩BP机爆炸事件带来的安全新挑战与反思

网安加社区【网安新声】栏目&#xff0c;汇聚网络安全领域的权威专家与资深学者&#xff0c;紧跟当下热点安全事件、剖析前沿技术动态及政策导向&#xff0c;以专业视野和前瞻洞察&#xff0c;引领行业共同探讨并应对新挑战的策略与可行路径。 9月17日&#xff0c;黎巴嫩境内发…

论文阅读与分析:Few-Shot Graph Learning for Molecular Property Prediction

论文阅读与分析&#xff1a;Few-Shot Graph Learning for Molecular Property Prediction 论文地址和代码地址1 摘要2 主要贡献3 基础知识Meta Learning1 介绍2 学习算法Step 1: What is learnable in a learning algorithm?Step 2&#xff1a;Define loss function for learn…

本地电脑基于nginx的https单向认证和双向认证(自制证书+nginx配置)保姆级

目录 1、背景 2、运行环境 3、工具下载 3.1、OpenSSL下载 3.2、nginx下载 4、制作https证书&#xff1a; 4.1、CA与自签名&#xff1a; 4.2、制作CA根证书&#xff08;公钥&#xff09; 4.3、制作服务端证书&#xff1a; 4.4、制作客户端证书&#xff1a; 4.5、制作…