POJ 2823 Sliding Window(滑动窗口) 线段树/deque/树状数组

题目vj地址:POJ 2823

题意:给出一个序列,长度为n,n最大1e6 。k表示区间长度。问区间长度为k的区间,最大值和最小值分别是多少。

分析: 区间最大值和区间最小值,很容易想到线段树和树状数组等等。由于n最大1e6 线段树查询一次的复杂度logn 所以时间上是没问题了。

先上个线段树AC代码 。。。。 后面还有deque的做法分析

时间:10313ms ,内存 44712kB

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <string>
#include <map>
#include <stack>
#include <queue>

#define INF 0x3f3f3f3f

using namespace std;

const int N=1e6+100;

typedef long long ll;

int n,k;

int a[N];
int x[N];
int y[N];

struct Node
{
	int l,r;
	int maxx;
	int minn;
}T[N<<2];

void pushup(int p)
{
	T[p].maxx=max(T[p<<1].maxx,T[p<<1|1].maxx);
	T[p].minn=min(T[p<<1].minn,T[p<<1|1].minn);
}

void build(int l,int r,int p)
{
	T[p].l=l,T[p].r=r;
	if(l==r)
	{
		T[p].maxx=a[l];
		T[p].minn=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,p<<1),build(mid+1,r,p<<1|1);
	pushup(p);

}

int query_max(int l,int r,int p)
{
	if(l<=T[p].l&&T[p].r<=r)
	{
		return T[p].maxx;
	}
	int mid=(T[p].l+T[p].r)>>1;
	int ans=-INF;
	if(l<=mid)ans=max(ans,query_max(l,r,p<<1));
	if(r>mid)ans=max(ans,query_max(l,r,p<<1|1));
	return ans; 
}

int query_min(int l,int r,int p)
{
	if(l<=T[p].l&&T[p].r<=r)
	{
		return T[p].minn;
	}
	int mid=(T[p].l+T[p].r)>>1;
	int ans=INF;
	if(l<=mid)ans=min(ans,query_min(l,r,p<<1));
	if(r>mid)ans=min(ans,query_min(l,r,p<<1|1));
	return ans; 
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	build(1,n,1);
	for(int i=1;i<=n;i++)
	{
		x[i]=query_max(i,i+k-1,1);
	}
	for(int i=1;i<=n;i++)
	{
		y[i]=query_min(i,i+k-1,1);
	}
	
	for(int i=1;i<=n-k+1;i++)
	{
		printf("%d%c",y[i],i==n-k+1?'\n':' ');
	}
	for(int i=1;i<=n-k+1;i++)
	{
		printf("%d%c",x[i],i==n-k+1?'\n':' ');
	}
	
	return 0;
}

为什么再说下deque的做法呢,因为空间,线段树的空间很大,如果出题者特意要卡内存的话,那么线段树就JJ了。

这里介绍deque的思想。

首先我们要知道,每次算区间的时候,整个区间k相当于移动了一格。那么我们没必要重新去计算。 我们每次去计算的时候,先判断下,队列顶的元素是不是超出我们的区间范围了,如果超出了,那么pop掉,因为超出区间范围,我们没必要算他了。如果当前元素大于队列顶部的那个元素,我们需要把队列顶部的那个元素弹出,因为后面最大值将不会是他了。判断好上面的情况后,我们需要把这个元素加到队列尾部,但是加的时候,我们需要去判断当前元素是不是大于尾部元素,如果大于尾部元素,也pop掉。 这是最大值的做法,最小值也雷同,只要改变符号即可

AC代码:
时间:9266ms ,内存:15768kB
deque的复杂度是O(n)
时间和内存都优于线段树

#include <iostream>
#include <algorithm>
#include <deque>
#include <cstdio>
#include <cstring>

using namespace std;

const int N=1e6+100;

int n,k;

struct Node
{
	int val;
	int pos;
}a[N];

deque<Node>dq;//计算最大值
deque<Node>dq2;//计算最小值 
int x[N];
int y[N];

void solve()
{
//处理最大值
	for(int i=1;i<=n;i++)
	{
		if(dq.empty())
		{//如果是空的,直接加入 
			dq.push_back(a[i]); 
		}
		else
		{
			Node top=dq.front();//访问头结点
			if(i-top.pos>=k)
			{
				dq.pop_front();
			}//如果超出区间直接删掉
			if(a[i].val>dq.front().val)
			{
				dq.pop_front();
			} 
			while(!dq.empty()&&a[i].val>dq.back().val)
			{
				dq.pop_back();
			}
			dq.push_back(a[i]); 
		} 
		x[i]=dq.front().val;
	}
	//处理最小值
	for(int i=1;i<=n;i++)
	{
		if(dq2.empty())
		{//如果是空的,直接加入 
			dq2.push_back(a[i]); 
		}
		else
		{
			Node top=dq2.front();//访问头结点
			if(i-top.pos>=k)
			{
				dq2.pop_front();
			}//如果超出区间直接删掉
			if(a[i].val<dq2.front().val)
			{
				dq2.pop_front();
			} 
			while(!dq2.empty()&&a[i].val<dq2.back().val)
			{
				dq2.pop_back();
			}
			dq2.push_back(a[i]); 
		} 
		y[i]=dq2.front().val;
	}
}

int main()
{
	scanf("%d%d",&n,&k);
	
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i].val);
		a[i].pos=i;
	}
	
	solve();
	
	for(int i=k;i<=n;i++)
	{
		printf("%d%c",y[i],i==n?'\n':' '); 
	}
	
	for(int i=k;i<=n;i++)
	{
		printf("%d%c",x[i],i==n?'\n':' '); 
	} 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值