记录算法Day11

代码随想录算法训练营第十一天|Leetcode 239 滑动窗口最大值,347 前k个高频元素

Leetcode 239.滑动窗口最大值

题目链接 239. 滑动窗口最大值
文章链接 https://programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html
视频链接 https://www.bilibili.com/video/BV1XS4y1p7qj

思路

一开始是用的暴力解法,遍历一遍窗口算出一个最大值,遍历n次,每个窗口的长度为k,所以时间复杂度是O(n*m),经过一番实践超时了。
开始学习题解的优秀解法:1.假设有一个数据结构,能使滑动窗口每次只向后移动一个元素,就会返回最大元素,有点类似先进先出,初步假定队列可以实现。2.单单是队列不太够,还要求最大元素,所以就要求这个队列的出口元素要是动态变化的,而且在push一个元素后,还要保持最大,单调队列可以实现,但是这个数据结构要我们自己实现,最主要的就是实现三个方法,pop(),push(),getMaxValue()。3.没必要去遍历每个窗口的元素,只需要维护每个滑动窗口可能的最大值即可。在实现时,就是push一个元素(滑动窗口右移一次),要比较之前的元素,只要比自己小就要把小的元素从队列中弹出,否则压入队列。
4.有一种情况,比如一个滑动窗口是(5,3,1),k=3,显然这三个数都应该在队列中,再往后遍历一个元素,这时队列的第一个元素就不得不弹出,对应的pop()方法是当前元素(nums[i-k]是队列的首元素时)。5.getMaxValue()就是返回队列的第一个元素即可,因为第一个元素是每个滑动窗口的最大值

代码

  • 看完题解思路之后写的
class Solution {

public:

    void pop(int val,deque<int> &que){

        //队列不为空,且要弹出的值正好是队列出口的值(最大值,队列中有k个元素不得不弹出去)

        if(!que.empty()&&val==que.front()){

            que.pop_front();

        }

    }

    void push(int val,deque<int> &que){

        //直到队列为空,或者直到压入的值小于等于队列的尾部元素循环结束,目的是创造单调性

        while(!que.empty()&&val>que.back()){

            que.pop_back();

        }

        que.push_back(val);

    }

    int getValueMax(deque<int> &que){

        //因为是单调递减的,所以队列入口值为每一次滑动窗口的最大值

        return que.front();

    }

    vector<int> maxSlidingWindow(vector<int>& nums, int k) {

        deque<int> que;

        vector<int> result(nums.size()-k+1);

        int m=0;

        //先把前k个元素压入

        for(int i=0;i<k;i++){

            this->push(nums[i],que);

        }

        result[m++] = this->getValueMax(que);

        for(int i=k;i<nums.size();i++){

            this->pop(nums[i-k],que);

            this->push(nums[i],que);

            result[m++] = this->getValueMax(que);

        }

        return result;

    }

};
  • 题解的(把单调队列用类封装起来,可读性更高)
class Solution {
private:
    class MyQueue { //单调队列(从大到小)
    public:
        deque<int> que; // 使用deque来实现单调队列
        // 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
        // 同时pop之前判断队列当前是否为空。
        void pop(int value) {
            if (!que.empty() && value == que.front()) {
                que.pop_front();
            }
        }
        // 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
        // 这样就保持了队列里的数值是单调从大到小的了。
        void push(int value) {
            while (!que.empty() && value > que.back()) {
                que.pop_back();
            }
            que.push_back(value);

        }
        // 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
        int front() {
            return que.front();
        }
    };
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue que;
        vector<int> result;
        for (int i = 0; i < k; i++) { // 先将前k的元素放进队列
            que.push(nums[i]);
        }
        result.push_back(que.front()); // result 记录前k的元素的最大值
        for (int i = k; i < nums.size(); i++) {
            que.pop(nums[i - k]); // 滑动窗口移除最前面元素
            que.push(nums[i]); // 滑动窗口前加入最后面的元素
            result.push_back(que.front()); // 记录对应的最大值
        }
        return result;
    }
};

Leetcode 347.前k个高频元素

题目链接 347. 前 K 个高频元素
文章链接https://programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html
视频链接 https://www.bilibili.com/video/BV1Xg41167Lz

思路

这道题和上面的题很像,先存储每个元素的出现次数,然后再排序返回前k个元素,即出现次数最多的k个元素。排序如果快排的话O(logn),但是没必要全部排序,只需要维护前k个最大的值即可。即每次入队列的元素要进行一次排序,根据题目,我们要pop()出最小的(剩下大的)——优先级队列。

代码

class Solution {

private:
//重载priority_queue的比较函数对象
    class mycomparison {

    public:
//operator()重载函数调用运算符
        bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {

            return lhs.second > rhs.second;

        }

    };

public:

    vector<int> topKFrequent(vector<int>& nums, int k) {

        vector<int> result(k);
//存储每个数的出现次数
        unordered_map<int,int> map;

        int m=0;
//优先队列,数据类型是pair<int,int>,存储数和其出现次数
        priority_queue<pair<int,int>,vector<pair<int,int>>,mycomparison> que;

        for(int i=0;i<nums.size();i++){

            map[nums[i]]++;

        }
//优先级队列(小顶堆)每次维护两个出现次数最大的数
        for(auto it=map.begin();it!=map.end();it++){

            que.push(*it);
//条件要大于k,每次pop()时队列中要有k+1个元素,pop()出一个后剩下k个,
            if(que.size()>k){

                que.pop();

            }

        }

        for(int i=k-1;i>=0;i--){

            result[i] = que.top().first;

            que.pop();

        }

        return result;

    }

};
  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我来回答你的问题。首先,广度优先搜索(BFS)是一种图搜索算法,可以用来解决一些问题,比如迷宫问题。在迷宫问题中,我们需要找到一条从起点到终点的最短路径。下面是用 Python 实现 BFS 解决迷宫问题的示例代码: ```python from collections import deque # 定义一个二维数组表示迷宫,0 表示可以走,1 表示障碍物 maze = [ [0, 1, 0, 0, 0], [0, 1, 0, 1, 0], [0, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 0, 0, 1, 0], ] # 定义起点和终点 start = (0, 0) end = (4, 4) # 定义四个方向,上下左右 directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] def bfs(maze, start, end): # 定义队列,初始时将起点加入队列 queue = deque([start]) # 定义 visited 集合,用于记录已经访问过的位置 visited = set([start]) while queue: # 取出队列中的第一个节点 node = queue.popleft() # 如果当前节点是终点,返回到达终点的最短距离 if node == end: return distance[node[0]][node[1]] # 对于当前节点,遍历它的四个方向 for direction in directions: # 计算出下一个节点的坐标 next_node = (node[0] + direction[0], node[1] + direction[1]) # 如果下一个节点不越界且没有访问过,并且可以走(maze[next_node[0]][next_node[1]] == 0),将它加入队列和 visited 集合 if 0 <= next_node[0] < len(maze) and 0 <= next_node[1] < len(maze[0]) and next_node not in visited and maze[next_node[0]][next_node[1]] == 0: queue.append(next_node) visited.add(next_node) # 如果没有找到到达终点的路径,返回 -1 return -1 # 计算每个位置到起点的最短距离 distance = [[float('inf') for _ in range(len(maze[0]))] for _ in range(len(maze))] distance[start[0]][start[1]] = 0 bfs(maze, start, end) ``` 在上面的代码中,我们首先定义了一个迷宫,然后定义了起点和终点。接着,我们定义了四个方向,上下左右。接下来,我们定义了 bfs 函数,用于实现广度优先搜索。在 bfs 函数中,我们首先定义了一个队列和 visited 集合,用于记录已经访问过的位置。然后,我们将起点加入队列和 visited 集合。接着,我们进行循环,取出队列中的第一个节点,遍历它的四个方向。如果下一个节点不越界且没有访问过,并且可以走,我们将它加入队列和 visited 集合。最后,如果没有找到到达终点的路径,返回 -1。 最后,我们计算每个位置到起点的最短距离,并调用 bfs 函数求解最短路径。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值