Given a sequence of data (it may have duplicates), a fixed-sized moving window, move the
window at each iteration from the start of the data sequence, such that
(1) the oldest data element is removed from the window and a new data element is pushed
into the window
(2) find the min&max of the data inside the window at each moving.
(以求窗口最大值为例,求最小值思想与最大值相同)
常规解法:对于每个窗口计算最大值,时间复杂度O(N*K) (N是序列长度,K是窗口大小)
优先队列:(假设数组为a[N])
窗口中的元素a[i],对于当前窗口的任意i<j的元素,如果a[i]<=a[j],由于a[i]在a[j]之前移出窗口,那么后续包含a[i]的窗口中不可能最大值为a[i],所以a[i]便没有意义。可以构造一个单调递减队列Queue,Queue中每个元素都在当前窗口,窗口移动时:
1)对于移入的元素,插入到Queue中的相应位置k,Queue[k]之前的元素都大于Queue[k],之后的元素都小于Queue[k],并且对于队列中k‘>k的元素,将在Queue[k]移出之前移出,并且Queue[k]>=Queue[k'],所以后续的包含Queue[k']的窗口中最大值不可能为Queue[k'],故可以将k之后的元素删除
2)对于移出的元素,如果Queue[front]元素的位置等于该移出窗口的元素位置,那么该元素需要从Queue中弹出(Queue中不仅要记录元素的值还要记录元素的位置)
//优先队列中元素
struct QueryElement
{
int value;
int pos;
};
void WindowMinMax(const int* pData, int Len, int K){
assert(pData!=NULL && Len>=K);
QueryElement** maxQuery = new QueryElement*[K+1]; //单调递减队列
int maxQFront=0, maxQTail=0; //循环队列,当front==tail时队列空,当(front+1)%Len==tail时队列满
QueryElement** minQuery = new QueryElement*[K+1]; //单调递增队列
int minQFront=0, minQTail=0;
for (int index=0; index<Len; index++){
int data = pData[index];
int popDataIndex = index-K; //当窗口满了之后,滑动窗口时有元素弹出
//根据弹出窗口的元素,更新队列
if (popDataIndex>=0){
if (maxQuery[maxQFront]->pos == popDataIndex){
delete maxQuery[maxQFront];
maxQuery[maxQFront] = NULL;
maxQFront = (maxQFront+1)%(K+1);
}
if (minQuery[minQFront]->pos == popDataIndex){
delete minQuery[minQFront];
minQuery[minQFront] = NULL;
minQFront = (minQFront+1)%(K+1);
}
}
//新加入到窗口的元素,插入到max和min队列中
int qIndex;
for (qIndex=maxQTail; qIndex!=maxQFront && maxQuery[(qIndex+K)%(K+1)]->value<=data; qIndex = (qIndex+K)%(K+1))
;
maxQuery[qIndex] = new QueryElement;
maxQuery[qIndex]->value = data; maxQuery[qIndex]->pos = index;
maxQTail = (qIndex+1)%(K+1);
for (qIndex=minQTail; qIndex!=minQFront && minQuery[(qIndex+K)%(K+1)]->value>=data; qIndex = (qIndex+K)%(K+1))
;
minQuery[qIndex] = new QueryElement;
minQuery[qIndex]->value = data; minQuery[qIndex]->pos = index;
minQTail = (qIndex+1)%(K+1);
if (index+1>=K){
cout<<"max="<<maxQuery[maxQFront]->value<<", min="<<minQuery[minQFront]->value<<endl;
}
}
for (int index = maxQFront; index!=maxQTail; index = (index+1)%(K+1)){
delete maxQuery[index];
maxQuery[index] = NULL;
}
for (int index = minQFront; index!=minQTail; index = (index+1)%(K+1)){
delete minQuery[index];
minQuery[index] = NULL;
}
delete[] maxQuery;
delete[] minQuery;
}