pyqt 只能打开子窗口一次_解决窗口滑动问题:窗口内最大值和最小值的更新结构...

定义一下窗口的概念:

ec006e6de58d3c85a2e34452673217c0.png

c889d1fa271cca76506f89ad11e6da13.png

3a4e1b94b06ff3a1eb05ebf25b63e9bd.png

4b8206b80806a8a0127ebcf3b41ec034.png

R往右动的时候,将有num从右进来。

l往右动的时候,num从左侧出来。

L<R。

窗口内最大值和最小值的更新结构:

功能:

1.告诉我窗口内的最大值。

2.维持窗口内的最大值更新。

复杂度为O(1)

双指针确定一个窗口

bcd1afc7b9856e92b5c4c88003f2fcda.png

只有两种移动方式。

1.L右移

2.R右移。

也即只有一种移动方向,向右移动。

同时,生成一个双端队列。deque

ed80d928e75a3c0553c01bfed204d4f6.png

保证这个双端队列从头到尾按照由大到小的顺序排列。

考虑R右移的情况:右移只能从尾部进入

第一个数字,R右移得到,直接进入。

8d2d2e9ebdc2869e00d53b665a8a19e7.png

当进入第二个数字5时,发现5大于3,此时,将3pop出来,将5放入。

134564dc49d87273162bb7ded19e4092.png

2可以直接进入。

6fb1007e08d9e49898715e6d5f31333d.png

4不能直接进入。

将2弹出,发现符合排序,进入4

f7c056ba77bc57e18d301281aa02cdff.png

可以发现,当R不断右移,窗口不断增大,窗口内最大值就是双端队列头部的元素。

考虑L动的情况:

L右移,那么就用一个元素不在窗口内,此时看看双端队列中,头部元素是不是这个离开的元素,如果是,将它弹出。

这是为什么呢?

这个双端队列代表什么?

成为最大值的可能性。

开始时,

db7af610dfdc262de98c70b377e37954.png

进入3,代表着,窗口L-R中的最大值就是3,因为窗口内就一个元素。

然后进入5,为啥要将3弹出呢?

因为5已经是这个窗口内的最大值了,并且,5在3之后出现,那么当L继续右移(L,R都只能右移),3一定会先离开双端队列,5后离开。无论如何,3都无法成为最大值。

然后再加入2,此时,不弹出5.因为2小于5。

这是为什么?

因为2虽然比5小,在当前窗口内无法成为最大值,但是当L右移之后,5离开了。2有成为最大值的潜力。

如果出现两个数字相同的元素进入。同样抛弃之前的元素,因为虽然数字相同,但是后面的数字晚离开。

既然你会了滑动窗口的最大值,那么滑动窗口的最小值怎么设计呢?

1.deque是从小到大顺序。

2.R右移,发现当前数字小于deque中数字,pop出来。

3.>时,将其加入

4.L右移时,pop top

下面是窗口内最大值的代码:

#include <iostream>
#include <vector>
#include <deque>
using namespace std;

vector<int> getWindowsMax(const vector<int> num, const int m) {
	vector<int> res;
	deque<int> index;//双端队列,按照头到尾由大到小的顺序排列。储存num中数字的下标。 
	/*if (num.empty() || m == 0) return res;*/

	for (int l = 0, r = 0; r < num.size(); ++r) {
		if (r - l >= m) {
			//窗口极限 此时要移动L右移。 
			if (index[0] == l) {
				//如果l的位置是双端队列中的头部元素,那么就要将头部元素pop出来
				index.pop_front();
			}
			++l;//L右移。 
		}

		while (!index.empty() && num[r] >= num[index.back()]) index.pop_back();

		index.push_back(r);

		if (r - l == m - 1) //构成了一个窗口
		//就求这个窗口内的最大值。
			res.push_back(num[index.front()]);


	}



	return res;
}

void test() {
	vector<int> v = { 4,3,5,4,3,3,6,7 };


	vector<int> res = getWindowsMax(v, 3);
	for (auto i:res) {
		cout << i << " ";
	}
}

int main() {


	test();

	return 0;
}

同理也能写出窗口内最小值的代码:

#include <iostream>
#include <vector>
#include <deque>
using namespace std;

vector<int> getWindowsMin(const vector<int> num, const int m) {
	vector<int> res;
	deque<int> index;//双端队列,按照头到尾由大到小的顺序排列。储存num中数字的下标。 
	/*if (num.empty() || m == 0) return res;*/

	for (int l = 0, r = 0; r < num.size(); ++r) {
		if (r - l >= m) {
			//窗口极限 此时要移动L右移。 
			if (index[0] == l) {
				//如果l的位置是双端队列中的头部元素,那么就要将头部元素pop出来
				index.pop_front();
			}
			++l;//L右移。 
		}

		while (!index.empty() && num[r] <= num[index.back()]) index.pop_back();

		index.push_back(r);

		if (r - l == m - 1) //构成了一个窗口
		//就求这个窗口内的最大值。
			res.push_back(num[index.front()]);


	}



	return res;
}

void test() {
	vector<int> v = { 4,3,5,4,3,3,6,7 };


	vector<int> res = getWindowsMin(v, 3);
	for (auto i:res) {
		cout << i << " ";
	}
}

int main() {


	test();

	return 0;
}

下面来看一道题:

给定一个数组,和一个数num,求满足这个数组子数组中max-min<=num的个数。

先来看看性质:

子数组个数有n^2个。

如果一个数组满足max-min<=num,那么这个数组中的所有子数组也满足这个性质。

因为,在子数组中,找到的max只能更小,min只能更大。

子数组不满足了,大数组也不满足,

因为大数组中的max 会更大,min会更小。

因此,从第一个元素开始,0位置,一直向外,求滑动窗口内的最大值和最小值。

判断是否满足,如果满足,继续进行。直至不满足。此时,r的位置+1就是满足性质的个数。

接下来从第二个位置开始。重复进行。

int windowMaxMin(vector<int> nums,int num) {
	cout<<"in function"<<endl;
	deque<int> maxIndex;
	deque<int> minIndex;
	int r = 0;
	int l=0;
	int cnt = 0;
	while (l < nums.size()) {
	cout<<"in loop"<<endl;
		while (r < nums.size()) {
			while (!maxIndex.empty() && nums[r] >= nums[maxIndex.back()]) {
				maxIndex.pop_back();
				

			}
			maxIndex.push_back(r);
			while (nums[r] <= nums[minIndex.back()]&& !minIndex.empty()) {
				 minIndex.pop_back();

			}

			minIndex.push_back(r);
			if (maxIndex.front() - minIndex.front() > num)  break;
			r++;
		}

		if (maxIndex[0] == l) maxIndex.pop_front();
		if (minIndex[0] == l) minIndex.pop_front();

		cnt += r-l;
		l++;
		cout<<cnt<<" ";
	}
	return cnt;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值