【数据结构】【队列】窗遍历数组,求窗内最小值

问题描述:有一个长度为n的数组,还有一个长度为k ≤ n的窗口。窗口一开始在最左边的位置,能看到元素{1, 2, 3, ..., k}。每次把窗口往右移动一个元素的位置,直到窗口的右边界到达数组的元素n。

案例:数组元素为{2, 7, 1, 6, -3, 2, 5, 0, 4},窗口长度k = 3。

输入包含n个元素的数组,以及窗口大小k,请依次给出各时刻中窗口中的最小值,用文字或伪代码描述你设计的算法并分析复杂度,算法复杂度越低越好。


最简单的方法:遍历 n-k+1次,每次让A[i]出队列,A[k+i-1]进队列,然后找出队列里最小值。这种方法的复杂度为: O(n-k)*O(k) ,不太好。

遍历n-k+1次是必须的,那么可以考虑减少findmin的次数。所以构造一个w_min队列:每次有数出窗口,如果它和w_min.front相等则队列pop;每次有数进入窗口,则比较w_min.back ,如果比队尾小则新数进队列;如果队列为空则在当前窗口进行一次复杂度为O(k)的findmin操作。这样就保证了队尾一定是窗口最小值。

算法实现:

#include <iostream>
#include <string.h>
#include <queue> 

using namespace std;

int main () {
    int n,k; cin>>n>>k;
    int B[n];
    k=k;
    for (int i=0 ;i<n;) cin>>B[i++];
    queue<int> w_min;
     for (int i=0 ;i<=n-k;i++){
        if(w_min.empty()) {
          int temp=i;
          for(int j=0;j<k;j++)
          if(B[temp]>B[j+i])temp=j+i;
          w_min.push(B[temp]);
        }
        else if(w_min.front()==B[i]) w_min.pop();
        if(w_min.back()>B[k+i-1]) w_min.push(B[k+i-1]);
        cout<<w_min.back()<<endl;
        }
    while(!w_min.empty()) w_min.pop();
    system("pause");        
    return 0;       
}

 

在循环中,除了 findmin,其他都是常数项的,而调用findmin的时机是队列为空,这种情况只有在窗口循环k次都没有新元素入队列才会发生,所以考虑最差的情况也是每k次循环调用1次。

所以,算法的时间复杂度为:

O(n-k)*O(1)+ O((n-k)/k)*O(k)=O(n-k)

O(n-k)*O(1)是除了循环中的常数项复杂度操作,O((n-k)/k)*O(k)是调用查询最小值操作次数与查询最小值复杂度乘积。

Guess you like

Origin blog.csdn.net/cyy010617/article/details/121166251