剑指 Offer 59 - I. 滑动窗口的最大值 - 力扣(Leetcode)

剑指 Offer 59 - I. 滑动窗口的最大值 - 力扣(Leetcode)

一.分析

方法一:

数组长度为1e5,k的大小为1e4,因此直接暴力计算会TLE。我们可以思考一个更复杂的问题:询问任意区间中的最值。对于该题,可以等价询问数组长度次的最值,如果能将每次询问的复杂度控制在logN以内,就可以解决该问题。
而对于区间最值我们可以使用线段树(logN),树状数组(logNlogN),稀疏数组(1)等数据结构。

  1. 树状数组
树状数组
class Solution {
public:
    int b[100005];
    int n;
    const int inf=-0x3f3f3f3f;
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int pos,vector<int>& a)
    {
        while(pos<=n)
        {
            b[pos]=a[pos-1];
            for (int i=1;i<lowbit(pos);i<<=1)
            {
                b[pos]=max(b[pos],b[pos-i]);
            }
            pos+=lowbit(pos);
        }
    }
    int query(int l,int r,vector<int>& a)
    {
        int ans=inf;
        while(l<=r)
        {
            ans=max(ans,a[r-1]);
            r--;
            while (l<=r-lowbit(r))
            {
                ans=max(ans,b[r]);
                r-=lowbit(r);
            }
        }
        return ans;
    }
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        n=nums.size();
        vector<int> ans;
        for (int i=1;i<=n;i++)
        {
            b[i]=inf;
        }
        for (int i=0;i<nums.size();i++)
        {
            update(i+1,nums);
        }
        // for (int i=1;i<=n;i++)
        // {
        //     cout<<b[i]<<" ";
        // }
        for (int i=k;i<=n;i++)
        {
            ans.push_back(query(i-k+1,i,nums));
        }
        return ans;
    }
};
  1. 稀疏数组
稀疏数组
class Solution {
public:
    const int static maxn=200005;
    int st[maxn][17];
    int Log2[maxn];
    int n;
    void init()
    {
        for (int i=1;i<n+10;i++)
            Log2[i]=Log2[i>>1]+1;
    }
    void prepro(vector<int>& nums)
    {
        for (int i=0;i<n;i++)
            st[i][0]=nums[i];
        //cout<<st[2][0]<<endl;
        for (int i=1;i<=Log2[n-1];i++)
        {
            for (int j=0;j<=n-(1<<i);j++)
            {
                st[j][i]=max(st[j][i-1],st[j+(1<<(i-1))][i-1]);
            }
        }
    }
    int query(int l,int r)
    {
        // cout<<l<<"----"<<r<<endl;
        // cout<<Log2<<endl;
        return max(st[l][Log2[r-l+1]-1],st[1+r-(1<<(Log2[r-l+1]-1))][Log2[r-l+1]-1]);
    }
    vector<int> maxSlidingWindow(vector<int>& nums, int k) { 
        n=nums.size();
        init();
        //cout<<st[2][0]<<endl;
        prepro(nums);
        vector<int> ans;
        for (int i=0;i<n-k+1;i++)
        {
            ans.push_back(query(i,i+k-1));
        }
        //ans.push_back(query(2,4));
        return ans;
    }
};

方法二:

考虑滑动窗口向右移动过程中下标为i和j的元素,i<j, 若nums[i] < nums[j],那么num[i]就不可能是滑动窗口中的最大值了。因为若i在滑动窗口中,那么j肯定在滑动窗口中。因此可以维护一个双向队列,当向右移动时,添加元素应该保证队列的严格单调递减(如违反,则从队尾删除元素,类似单调栈)。同时还要保证队列中的元素在滑动窗口中,若不在,从队头删除元素。
复杂度分析:数组中的每个元素都入队一次,出队一次,时间复杂度为O(n),空间复杂度为双向队列的长度O(k)

双向队列
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<pair<int,int>> q;
        vector<int> ans;
        for (int i=0;i<nums.size();i++)
        {
            while (q.size() and q.back().second<nums[i])
            {
                q.pop_back();
            }
            q.push_back(pair(i,nums[i]));
            while (q.size() and q.front().first<i-k+1)
            {
                q.pop_front();
            }
            if (i>=k-1)
                ans.push_back(q.front().second);
        }
        return ans;
    }
};
posted @ 2022-11-13 11:13  sjwsjwsjwsjw1234  阅读(28)  评论(0)    收藏  举报