滑动窗口法与剑指offer:和为S的连续正数数列 与 和为S的两个数字

和为S的连续正数数列

输出描述:

输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

 

一开始看到这个题没有什么思路,实际上可以利用滑动窗口的思想来解决。

 

left指向窗口左侧,right指向窗口右侧,一开始窗口不满足要求,因此要使窗口满足和为sum。当窗口和小于sum,要使他增大,因此移动窗口右侧:tmp+=++right;如果窗口和大于sum,要使他减小,则应该前移做窗口:tmp-=(left++);

每当窗口满足要求,将当前结果输出,并再度破坏窗口平衡条件,tmp+=++right,让窗口遍历每一种情况。

 

滑动窗口法非常适合这种数组形式的问题,大概流程为:

1.使窗口满足条件

2.输出结果

3.破坏窗口平衡条件,使窗口右滑

4.持续1~3直到窗口到最右侧

 

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        int left=1,right=2;
		int tmp=3;
		vector<vector<int>> ans;
		while(left<right&&right<=(sum+1)/2)
		{
			while(tmp!=sum)
			{
				if(tmp==sum)
					break;
				else if(tmp<sum)
					tmp+=++right;
				else
					tmp-=(left++);
			}
			if(left==right)
				break;
			vector<int> arr;
			for(int i=left;i<=right;i++)
				arr.push_back(i);
			ans.push_back(arr);
			tmp+=++right;
		}
		return ans;
    }
};

  

 和为S的两个数字  

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

 

这个题也可以利用滑动窗口法,只是窗口中真正的有效信息是low与high,或者换句话说,真正的窗口不是[low,high]而是[0,low]与[high,end]组成的区间。

当和过大,则应该缩小和,即将high--,而当和过小,则应扩大和,令low++,最终直到窗口大小等于整个数组大小,也就是[0,end]。

 

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
		if(array.empty())
			return vector<int>();
		int low=0,high=array.size()-1;
		auto& num=array;
		int g_min=INT_MAX;
		int num1=0,num2=0;
		while(low<high)
		{
			int tmp=num[low]+num[high];
			if(tmp==sum)
			{
				if(num[low]*num[high]<g_min)
				{
					num1=num[low];
					num2=num[high];
					g_min=tmp;
				}
				high--;
			}
			else if(tmp<sum)
				low++;
			else
				high--;
		}
		vector<int> ans;
		if(INT_MAX!=g_min)
		{
			ans.push_back(num1);
			ans.push_back(num2);
		}
		return ans;
    }
};

  

滑动窗口更像是另一种角度的遍历,传统的我们使用单个指针从0到end亦或是从end到0,这种遍历方式无法满足要求,而当我们该用滑动窗口,在窗口移动的过程中,我们其实也在遍历这个数组,当滑动窗口到头或滑动窗口扩大到整个数组的时候,我们遍历也就结束了。

滑动窗口的方式相比这种单指针遍历,它在滑动的过程中保存住了信息,像单指针,在位置i与位置0其实没有区别,我们并不知道关于有关这个数组的信息,如果采用暴力搜索的方法,每次移动到位置i后,在位置i上用另一个指针遍历整个数组,得到了相关信息,但一旦我们将i移动到i+1,这部分信息就丢失了,相当于又要重新暴力的在搜索一遍这个数组。

而滑动窗口的方式,我们每次移动,比如从i到i+1,实际上我们都保存住了i的信息,比如第一题,相比于暴力搜索,我们保留住了i的信息,在i+1位置,我们仍然知道上一个滑动窗口的和,从而不用在i+1处在重新暴力搜索,去找一个位置k,使k~i+1的连续和逼近S而去看看他是否满足要求,我们只要在上一个窗口的基础上去改变,现在发现连续和变大了,我们要使它变小,只要移动左侧指针就可以了,因为我们存有i位置IDE信息;又比如第二题,相比于暴力搜索,我们在i位置保存住了当前这两个数的和,从而当位置改变时我们不用再重新搜索另一个数,使与被搜索到的数的和去逼近S,因为我们已经保存住了上一个窗口的信息,我们知道,现在位置改变,和变小了,我只要在前一个窗口的基础上使这个和变大,也就是移动窗口左侧的指针就可以了。

posted @ 2019-08-23 09:44  李湘沅  阅读(176)  评论(0编辑  收藏  举报