代码随想录算法训练营day13 | 239. 滑动窗口最大值,347. 前 K 个高频元素

239. 滑动窗口最大值

目录

239. 滑动窗口最大值

347. 前 K 个高频元素


239. 滑动窗口最大值

难度:hard

类型:队列,单调队列,滑动窗口

思路:

        构造单调队列,维护大小为k的队列。队列里的元素始终是单调递减的,处于队列出口的元素始终是最大值。使用Deque来维护这个单调队列。

代码:

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int len = nums.length;
        if (len == 1) {
            return nums;
        }
        // 存储答案的数组,有len-k+1个窗口,每个窗口一个最大值
        int[] ans = new int[len - k + 1];
        // ans数组的计数
        int num = 0;
        // 声明单调队列
        MyQueue queue = new MyQueue();
        // 先将第一个窗口的k个元素添加进去
        for (int i = 0; i < k; i++) {
            queue.push(nums[i]);
        }
        ans[num++] = queue.getMaxValue();

        for (int i = k; i < len; i++) {
            // pop出窗口中最左边的元素
            queue.pop(nums[i - k]);
            queue.push(nums[i]);
            ans[num++] = queue.getMaxValue();
        }

        return ans; 
    }
}

// 单调队列
class MyQueue {
    // 双端队列维护单调队列
    public Deque<Integer> deque = new LinkedList<>();

    // 添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
    // 保证队列元素单调递减
    // 比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
    public void push(int val) {
        while (!deque.isEmpty() && deque.peekLast() < val) {
            deque.pollLast();
        }
        deque.offerLast(val);
    }
    // 判断窗口左端元素是否是队列的左出口元素,若是则pop;反之则说明窗口左端元素已经被pop了
    public void pop(int val) {
        if (!deque.isEmpty() && val == deque.peekFirst()) {
            deque.pollFirst();
        }
    }
    // 队列里的前出口元素即为队列里的最大值
    public int getMaxValue() {
        return deque.peekFirst();
    }

}

复杂度分析:

  • 时间复杂度: O(n)
  • 空间复杂度: O(k)

347. 前 K 个高频元素

难度:medium

类型:堆,优先队列

思路:

        参考文章:(51条消息) LeetCode.347. 前 K 个高频元素_Chamberlain T的博客-优快云博客

 代码随想录 (programmercarl.com)

        需要掌握PriorityQueue优先队列和Map.Entry键值对;

        小顶堆的方法是符合时间复杂度优于O(nlogn)的,因为小顶堆方法只需要维持k个最大元素,所以其时间复杂度为O(nlogk).

        利用hashmap来记录数组中的元素和对应的出现次数,用优先队列来找出频率最高的k个元素。

代码:

// 基于大顶堆实现
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num: nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        // 构造大顶堆(o2-o1),直接存储Entry键值对
        PriorityQueue<Map.Entry<Integer, Integer>> pd = new PriorityQueue<>((o1, o2) -> o2.getValue() - o1.getValue());
            // 将map中存储的键值对放入优先队列中
            for (Map.Entry<Integer, Integer> entry: map.entrySet()) {
                pd.offer(entry);
        }
        int[] ans = new int[k];
        for (int i = 0; i < k; i++) {
            ans[i] = pd.poll().getKey();
        }
        return ans;
    }
}

// 基于小顶堆实现
// 时间复杂度O(nlogk),空间复杂度O(n);因为只需要维持大小为k的小顶堆
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num: nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
    
        PriorityQueue<int[]> pd = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);
        for (Map.Entry<Integer, Integer> entry: map.entrySet()) {
            if (pd.size() < k) {
                pd.offer(new int[]{entry.getKey(), entry.getValue()});
            } else {
                // 优先队列元素个数超出k个,把最小的元素弹出:比较新元素的小顶堆堆顶元素
                if (entry.getValue() > pd.peek()[1]) {
                    pd.poll();
                    pd.offer(new int[]{entry.getKey(), entry.getValue()});
                }
            }
        }
        int[] ans = new int[k];
        for (int i = 0; i < k; i++) {
            ans[i] = pd.poll()[0]; 
        }
        return ans;
    }
}

复杂度分析:

大顶堆方法:时间复杂度O(nlogn),空间复杂度O(n)

小顶堆方法:时间复杂度O(nlogk),空间复杂度O(n);因为只需要维持大小为k的小顶堆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值