【代码随想录37期】Day13 滑动窗口最大值、前K个高频元素

26 篇文章 0 订阅
25 篇文章 0 订阅

滑动窗口最大值

代码随想录

239. 滑动窗口最大值 - 力扣(LeetCode)

v1.0:直接双指针 固定滑动窗口 暴力超时
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> result;
        
        for(int left = 0, right = left + k - 1;right<nums.size();left++, right++)
        {
            int maxNum = nums[left];
            for(int i = left;i<=right;i++)
            {
                if(nums[i]>maxNum)
                {
                    maxNum = nums[i];
                }
            }
            result.push_back(maxNum);
        }
        return result;
    }
};

v2.0:使用定义的单调队列,单调队列需要用deque进行定义,因为需要从队列尾部取出元素,普通队列做不到这一点
在遍历时,先取出窗口外的元素,如果是队头元素则去除,这里如果不是队头元素就先留里面
如果是新元素与单调队列头进行比较,大于则清空队列,队列头改为新元素,小于则再比较是否大于单调队列尾
如果也不大于尾就加进来,大于尾就从尾部去掉小于新元素的值,再把新元素加进来
class Solution {
public:
    struct MyQueue
    {
        public:
        dequeuint> q;

        void pop(int value)
        {
            if(!q.empty()&&value==q.front())
            {
                q.pop_front();
            }
        }
        void push(int value)
        {
            while(!q.empty()&&value>q.back())
            {
                q.pop_back();
            }
            q.push_back(value);
        }

        int getMax()
        {
            return q.front();
        }
    };
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue q;
        vector<int> result;
        for(int i = 0; i<k;i++)
        {
            q.push(nums[i]);
        }
        result.push_back(q.getMax());
        for(int i = k; i< nums.size();i++)
        {
            q.pop(nums[i-k]);
            q.push(nums[i]);
            result.push_back(q.getMax());
        }
        return result;
    }
};
struct MyQueue {
private:
    deque<int> q;
    // 设计的核心思想:push进来的时候,从尾巴开始比,把小的都去掉。因为新来的后面有可能变成最大值
    // 但是老的比新来的还小的就不可能变成最大值了
    // pop的时候,如果是最大值,那就要pop,否则不用管
    // 因为这个队列只需要返回最大值
public:
    void Pop(int value)
    {
        if (!q.empty() && value == q.front())
        {
            q.pop_front();
        }
    }

    void Push(int value)
    {
        while (!q.empty() && value > q.back())
        {
            q.pop_back();
        }
        q.push_back(value);
    }

    int getMax()
    {
        return q.front();
    }
};

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    vector<int> ret;
    MyQueue q;
    for (int i = 0; i < k; i++)
    {
        q.Push(nums[i]);
    }
    ret.emplace_back(q.getMax());
    for (int i = k; i < nums.size(); i++)
    {
        q.Pop(nums[i - k]);
        q.Push(nums[i]);
        ret.emplace_back(q.getMax());
    }
    return ret;
}

前K个高频元素

代码随想录

347. 前 K 个高频元素 - 力扣(LeetCode)

思路

  1. 统计元素频率
  2. 对元素按照频率排名
  3. 找出前K个

统计元素频率

使用map即可

对元素按照频率排名

使用容器适配器:队列

因为要排名,使用优先级队列priority_queue

优先级队列是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。

优先级队列内部元素是自动依照元素的权值排列

缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)

堆是完全二叉树,当树中每个节点都不小于其左右孩子时是“大顶堆”,当树中每个节点都不大于其左右孩子时是“小顶堆”,一个简单的具体实现就是使用priority_queue

若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

在这里插入图片描述
在这里插入图片描述

使用快排VS使用优先级队列

如果不使用优先级队列而是使用快排,需要先将map映射为vector,再对整个数组进行快排

而使用优先级队列只需要维护k个有序的序列就可以了

为什么用的是小顶堆

如果使用大顶堆,队列的特点是只能从队头出,那样最大的元素就会被弹出,我们需要的就是最大的K个元素,所以应该使用小顶堆把最小的元素弹出

我的解法

v1.0:想先用排序来解决一下,在网上看到了如何把map转换为vector进行排序:创建一个以pair为元素的vector,再使用自定义的sort进行排序即可

class Solution {
public:
    bool static cmp(const pair<int, int>& a, const pair<int, int>& b)
    {
        return a.second > b.second;
    }
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> umap;
        
        for(auto num:nums)
        {
            umap[num]++;
        }
        vector<pair<int, int>> forSort(umap.begin(), umap.end());
        sort(forSort.begin(), forSort.end(), cmp);
        vector<int> result;
        while(k--)
        {
            result.push_back(forSort[k].first);
        }
        return result;
    }
};

v2.0:使用priority_queue,这家伙居然要三个参数:元素类型、所适配的容器类型、重载运算符,详见

优先级队列

class Solution {
public:
    bool static cmp(const pair<int, int> &a, const pair<int, int> &b)
    {
        return a.second > b.second;
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> umap;
        for(auto num:nums)
        {
            umap[num]++;
        }
        priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> pri_que(cmp);

        for(unordered_map<int, int>::iterator it = umap.begin(); it!=umap.end(); it++)
        {
            pri_que.push(*it);
            if(pri_que.size()>k)
            {
                pri_que.pop();
            }
        }
        vector<int> result;
        while(k--)
        {
            result.push_back(pri_que.top().first);
            pri_que.pop();
        }
        return result;
    }
};

有个小疑问:

 bool static cmp(const pair<int, int> &a, const pair<int, int> &b)
    {
        return a.second > b.second;
    }

priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> pri_que(cmp);

class Cmp{
        public:
        bool operator()(const pair<int, int> &a, const pair<int, int> &b)
        {
            return a.second>b.second;
        }
    };
priority_queue<pair<int, int>, vector<pair<int, int>>, Cmp> pri_que;

有什么区别?

第一个的话就是把函数指针作为参数传进pri_que

第二个是重载括号运算符,说明pri_que内部会这样调用Cmp(a,b);

bool static cmp(pair<int, int> p1, pair<int, int> p2)
{
    return p1.second > p2.second;
}

vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> umap;
    for (int i : nums)
    {
        umap[i]++;
    }
    vector<pair<int, int>> forSort(umap.begin(), umap.end());
    sort(forSort.begin(), forSort.end(), cmp);
    vector<int> ret;
    for (int i = 0; i < k; i++)
    {
        ret.emplace_back(forSort[i].first);
    }
    return ret;
}

关键点

指定sort的对比方法时报错error:reference to non-static member function must be called

我们在力扣写代码时,是在solution类里面,我们直接在这里面写的函数是该类的成员函数,非静态成员函数有一个隐藏的隐式指针参数指向调用它们的对象,例如前面的cmp函数

当我们尝试将非静态成员函数作为函数指针传递给**STL算法(如sort函数)**时,由于多一个this参数无法使用。因此我们需要将非静态成员函数转换为静态成员函数或全局函数,例如下面改成全局函数(力扣的solution类之外)

bool cmp(pair<int, int> p1, pair<int, int> p2)
{
    return p1.second > p2.second;
}
class Solution {
public:
    

vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> umap;
    for (int i : nums)
    {
        umap[i]++;
    }
    vector<pair<int, int>> forSort(umap.begin(), umap.end());
    sort(forSort.begin(), forSort.end(), cmp);
    vector<int> ret;
    for (int i = 0; i < k; i++)
    {
        ret.emplace_back(forSort[i].first);
    }
    return ret;
}
};
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值