算法训练 day13 | 239. 滑动窗口最大值 347.前 K 个高频元素

239. 滑动窗口最大值

题目链接:滑动窗口最大值

视频讲解:单调队列正式登场!

        对于此题我们要自己实现一个单调队列,对头为最大值。那么怎么设计呢?主要是对于队列的push和pop合理设计,即能实现它的单调性。

其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。所以单调队列不仅仅是对窗口里的值进行排序。

pop和push的规则:

1、push:如果push的元素大于队尾的元素,那么就将队尾元素弹出,直到push的元素小于等于队尾的元素为止;

2、pop:如果需要移除的元素等于队头的元素(大于的情况push中已经处理了),那么队列弹出元素,否则不进行操作。

保证上述规则,每次窗口移动时,队头的元素就一直是当前窗口的最大值。

// 时间复杂度: O(n)
// 空间复杂度: O(k) 空间复杂度因为我们定义一个辅助队列,所以是O(k)
class Solution {
public:
    class MyQueue // 单调队列
    {
    public:
        deque<int> que; // 用duque实现单调序列

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

        void pop(int value)
        {
            if (!que.empty() && value == que.front()) // que的front是里面最大的元素,pop_front要满足value >= que.front,>的情况再push中处理了,pop中只用处理相等的情况
            {
                que.pop_front();
            }
        }

        int front()
        {
            return que.front();
        }
    };

    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
       MyQueue que;
       vector<int> res;
       for (int i = 0; i < k; ++i)
       {
           que.push(nums[i]);
       }
       res.push_back(que.front()); // 单调队列头元素放入结果数组中
       for (int i = k; i < nums.size(); ++i)
       {
           que.pop(nums[i - k]);
           que.push(nums[i]);
           res.push_back(que.front());
       }
       return res;
    }
};

347.前 K 个高频元素

题目链接:前 K 个高频元素

视频讲解:优先级队列正式登场!

        根据题意,我们需先统计元素出现的频率,显然用map比较方便。然后就是对频率进行排序取出前k大的,这就引出了优先级队列(是个堆)。

定义优先级队列

1、使用默认比较函数:

std::priority_queue<int> pq;

创建了一个存储整数类型的优先级队列,底层容器使用默认的std::vector<int>,默认使用 < 运算符进行比较,是由大到小。

2、使用自定义比较函数或函数对象:

struct Compare {
    bool operator()(int a, int b) {
        // 自定义的比较逻辑
        // 返回 true 表示 a 的优先级高于 b
        // 返回 false 表示 a 的优先级低于或等于 b
    }
};
std::priority_queue<int, std::vector<int>, Compare> pq;

这种方式创建了一个存储整数类型的优先级队列,底层容器使用 std::vector<int>,并使用自定义的 Compare 结构体或类来定义元素的优先级比较方式。Compare 需要重载 () 运算符来实现比较逻辑。

3、使用lambda表达式:

auto comp = [](int a, int b) {
    // 自定义的比较逻辑
    // 返回 true 表示 a 的优先级高于 b
    // 返回 false 表示 a 的优先级低于或等于 b
};
std::priority_queue<int, std::vector<int>, decltype(comp)> pq(comp);

这种方式创建了一个存储整数类型的优先级队列,底层容器使用 std::vector<int>,并使用lambda 表达式来定义元素的优先级比较方式。decltype(comp) 用于推导 lambda 表达式的类型,并将其作为模板参数传递给 std::priority_queue

注意:题目要求前k个高频元素,不要直接想着就用大顶堆了,大顶堆把最大的值放在最上面,也是最先弹出去的,那就找不到前k个了,所以要用小顶堆。保证优先级队列大小为k,这样每次弹出去的都是较小的,留到最后的k个值就是所求结果。

// 时间复杂度: O(nlogk)
// 空间复杂度: O(n)
class Solution {
public:
    struct mycomparison{
        bool operator() (const pair<int, int>& l, const pair<int, int>& r)
        {
            return l.second > r.second; // 比较第二个值,出现的次数 左大于右是小顶堆
        }
    };

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> map; // key为数组里的数,value为出现的次数
        for (int i = 0; i < nums.size(); ++i)
        {
            map[nums[i]]++;
        }
        priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pq; // 优先级队列
        for (auto it = map.begin(); it != map.end(); ++it)
        {
            pq.push(*it); // map放入队列中
            if (pq.size() > k) // pq大小定为k,超过k弹出
            {
                pq.pop();
            }
        }

        vector<int> res(k); // 别忘初始化大小,存放结果
        for (int i = k - 1; i >= 0; --i) // 由大到小存储,在优先级队列中是由小到大的
        {
            res[i] = pq.top().first; // 存放key,对应数组中的元素
            pq.pop();
        }
        return res;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值