1. 程式人生 > >面試題72:滑動視窗的最大值

面試題72:滑動視窗的最大值

題目:

給定一個數組和滑動視窗的大小,請找出所有滑動窗口裡的最大值。

思路:

方法一:暴力法

視窗大小為K,則每次找到一個視窗最大值的時間複雜度為O(K),總時間複雜度:O(NK)

方法二:

仔細觀察,發現視窗滑動其實就是佇列先移出一個元素,再新增一個元素,我們可不可以在這個過程中用O(1)的時間來找到佇列裡最大的那個元素?

我們用兩個棧實現過O(1)時間找出棧中最小的元素:面試題25:包含min函式的棧

綜上可知,我們可以用兩個棧來實現佇列,由於可以用O(1)時間找到棧的最大值,那麼也就可以用O(1)時間找到佇列的最大值。

方法三:用最大堆實現

建立一個有K個元素的最大堆,每次插入一個元素的時間複雜度:O(lgK),獲得最大值的時間複雜度為:O(1)

由於優先佇列預設情況下是一個最大堆,我們用優先佇列來實現。

#include <iostream>    
#include <vector>    
#include <stack>
#include <queue>
#include <algorithm>  //堆演算法
#include <functional> //仿函式
using namespace std;

typedef pair<int, int> Pair;

vector<int> MaxSlidingWindow(vector<int> A, int K)
{
	int size = A.size();
	vector<int> re;
	if (size < K) re;
	for (int i = 0; i <= size-K; i++)
		re.push_back(0);
	priority_queue<Pair> Q;
	for (int i = 0; i < K; i++)
		Q.push(Pair(A[i],i));
	for (int i = K; i < size; i++)
	{
		Pair p = Q.top();
		re[i - K] = p.first;
		while (p.second <= i - K)  //注意這個地方
		{
			Q.pop();
			p = Q.top();
		}
		Q.push(Pair(A[i], i));    //其實堆中的元素最終超過K個
	}
	re[size - K] = Q.top().first;
	return re;
}

int main()
{
	int arr[] = { 2, 3, 4, 2, 6, 2, 5, 1 };
	vector<int>A(arr, arr + 8);
	vector<int> re = MaxSlidingWindow(A, 3);
	for (int i = 0; i < re.size(); i++)
		cout << re[i] << " ";
	cout << endl;
	return 0;
}
上面感覺有問題,時間複雜度肯定超過O(NlgK)

方法四:用deque實現

使用雙向佇列可以保證,最大值總是在佇列首部,且佇列中的元素總是從大到小排列(插入元素時,如果尾部的元素比插入的元素小,先將尾部的元素出佇列,再插入)。

當遇到比當前滑動視窗最大值更大的值時,先將佇列清空,再將新的最大值插入到佇列。

每次移動需要判斷當前最大值是否在有效範圍,如果不是,需將其從佇列中刪除。

如何判斷滑動視窗是否包含一個數字呢?

應該在佇列裡存入數字在數組裡的下標,而不是數值。當一個數字的下標與當前處理的數字的下標之差大於或者等於滑動視窗的大小時,這個數字已經從視窗滑出,可以從佇列中刪除了。

時間複雜度:O(n)

#include <iostream>    
#include <vector>    
#include <stack>
#include <queue>
#include <algorithm>  //堆演算法
#include <functional> //仿函式
using namespace std;

vector<int> MaxSlidingWindow(vector<int> A, int K)
{
	int size = A.size();
	vector<int> re;
	if (size < K) re;
	for (int i = 0; i <= size-K; i++)
		re.push_back(0);
	deque<int> indexQ;
	for (int i = 0; i < K; i++)
	{
		while (!indexQ.empty() && A[indexQ.back()] <= A[i]) indexQ.pop_back();
		indexQ.push_back(i);
	}
	for (int i = K; i < size; i++)
	{
		re[i - K] = A[indexQ.front()];
		while (!indexQ.empty() && A[i] >= A[indexQ.back()]) indexQ.pop_back();
		if (!indexQ.empty() && indexQ.front() <= (i - K)) indexQ.pop_front();
		indexQ.push_back(i);
	}
	re[size - K] = A[indexQ.front()];
	return re;
}

int main()
{
	int arr[] = { 2, 3, 4, 2, 6, 2, 5, 1 };
	vector<int>A(arr, arr + 8);
	vector<int> re = MaxSlidingWindow(A, 3);
	for (int i = 0; i < re.size(); i++)
		cout << re[i] << " ";
	cout << endl;
	return 0;
}