題目描述
給定一個數組和滑動窗口的大小,找出所有滑動窗口裏數值的最大值。例如,如果輸入數組{2,3,4,2,6,2,5,1}及滑動窗口的大小3,那麼一共存在6個滑動窗口,他們的最大值分別爲{4,4,6,6,6,5}; 針對數組{2,3,4,2,6,2,5,1}的滑動窗口有以下6個: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
解法一:暴力法,時間複雜度O(nk)
- 掃描每個滑動窗口的所有數字並找出其中的最大值。
- 如果滑動窗口的大小爲K,則需要O(k)時間找到最大值。
- 對於長度爲 n 的輸入數組,算法的時間複雜度爲O(nk)。
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int>res;
if(num.size()<size)
return res;
for(int i = 0, j=size-1; i<num.size()-size+1 && j<num.size(); i++, j++)
{
int M = num[i];
for(int k = i; k<=j;k++) //遍歷窗口的內所有的值,找出最大值。
{
M = max(M, num[k]);
}
res.push_back(M);
}
return res;
}
};
解法二:最大堆,時間複雜度O(nlogk)
由於要考慮最大堆堆頂數字是否超過了滑動窗口的範圍,所以最大堆中存放pair類型,第一個爲數組的數字,第二個是數組中數字對應的下標值。
- 採用最大堆結構
- 每次遍歷時,向堆中插入對應的pair,並檢查堆頂pair類型的數字是否超過華東窗口的範圍。沒有超出,則取堆頂pair類型的第一個位置的數據。
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int>res;
if(num.size()<size || size <= 0)
return res;
priority_queue<pair<int, int> >q;
for(int i=0; i<size; i++)
{
q.push(make_pair(num[i], i));
}
res.push_back(q.top().first);
for(int i=size; i<num.size(); i++)
{
q.push(make_pair(num[i], i));
pair<int, int> p= q.top();
while((i - p.second)>=size)
{
q.pop();
p = q.top();
}
res.push_back(p.first);
}
return res;
}
};
解法三:雙端隊列(deque),時間複雜度O(n)
算法思想:藉助一個輔助雙端隊列,步驟如下:
- 如果隊列爲空,則當前數字入隊列。
- 如果隊列不爲空且當前數字大於等於隊尾數字,則隊尾數字出隊直到隊尾元素小於當前待入隊的數字,或者在隊尾數字出隊的過程中,隊列爲空,則當前數字入隊。
- 如果當前數字小於隊尾數字,則當前數字入隊尾。
- 如果隊列頭部數字超出滑動窗口的範圍(比如,隊列頭部數字在數組中的位置是第一個,但是滑動窗口的範圍在第二個到第四個,那麼隊列頭部數字直接就可以刪除),則刪除隊列頭部數字(pop_front())。
由於要考慮隊列頭部數字是否超過了滑動窗口的範圍,所以隊列中存放的元素不能是原來的數組的數字,而應該是數組中數字的下標值。
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int>res;
if(num.size() < size || size <= 0)
return res;
deque<int>index;
for(unsigned int i=0; i<size; i++)
{
while(!index.empty() && num[i] >= num[index.back()])
index.pop_back();
index.push_back(i);
}
for(unsigned int i=size; i<num.size(); i++)
{
res.push_back(num[index.front()]);
while(!index.empty() && num[i] >= num[index.back()])
index.pop_back();
if(!index.empty() && index.front()<=(int)(i - size) )//如果隊列頭部數字超出滑動窗口的範圍,則刪除隊列頭部數字
index.pop_front();
index.push_back(i);
}
res.push_back(num[index.front()]);//滑到最後一個數字時,循環內部並沒有執行讀隊頭操作,所以這裏要留意下。
return res;
}
};
或者可以做如下修改。只是調整了其中兩行代碼的順序
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int>res;
if(num.size() < size || size <= 0)
return res;
deque<int>index;
for(unsigned int i=0; i<size; i++)
{
while(!index.empty() && num[i] >= num[index.back()])
index.pop_back();
index.push_back(i);
}
res.push_back(num[index.front()]); // #####################
for(unsigned int i=size; i<num.size(); i++)
{
while(!index.empty() && num[i] >= num[index.back()])
index.pop_back();
if(!index.empty() && index.front()<=(int)(i - size) )
index.pop_front();
index.push_back(i);
res.push_back(num[index.front()]);// ###########################
}
return res;
}
};