滑动窗口法与剑指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,因为我们已经保存住了上一个窗口的信息,我们知道,现在位置改变,和变小了,我只要在前一个窗口的基础上使这个和变大,也就是移动窗口左侧的指针就可以了。