剑指offer- 面试题59-1:滑动窗口的最大值

题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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)

  1. 扫描每个滑动窗口的所有数字并找出其中的最大值。
  2. 如果滑动窗口的大小为K,则需要O(k)时间找到最大值。
  3. 对于长度为 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类型,第一个为数组的数字,第二个是数组中数字对应的下标值。

  1. 采用最大堆结构
  2. 每次遍历时,向堆中插入对应的pair,并检查堆顶pair类型的数字是否超过华东窗口的范围。没有超出,则取堆顶pair类型的第一个位置的数据。
class Solution {
public:
    vector<int> maxInWindows(vector<int>& nums, int k) {
        vector<int>value;
        if(nums.size()==0 || k<=0 || k>nums.size())
            return value;
        priority_queue<pair<int, int>>q;
        for(int i=0; i<k; i++)
        {
            q.push(make_pair(nums[i], i));
        }
        value.push_back(q.top().first);
        for(int i=k; i<nums.size(); i++)
        {
            while(i - q.top().second > k-1 && !q.empty())
                q.pop();
            q.push(make_pair(nums[i], i));
            value.push_back(q.top().first);
        }
        return value;
        
    }
};

解法三:双端队列(deque),时间复杂度O(n)

算法思想:借助一个辅助双端队列,步骤如下:

  1. 如果队列为空,则当前数字入队列。
  2. 如果队列不为空且当前数字大于等于队尾数字,则队尾数字出队直到队尾元素小于当前待入队的数字,或者在队尾数字出队的过程中,队列为空,则当前数字入队。
  3. 如果当前数字小于队尾数字,则当前数字入队尾。
  4. 如果队列头部数字超出滑动窗口的范围(比如,队列头部数字在数组中的位置是第一个,但是滑动窗口的范围在第二个到第四个,那么队列头部数字直接就可以删除),则删除队列头部数字(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;
    }
};

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值