算法学习 day13

239. 滑动窗口最大值

  • 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

    返回 滑动窗口中的最大值

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7
示例 2:

输入:nums = [1], k = 1
输出:[1]
思考
  • 不愧为hard,思想理解,代码还是很难写出来。。
  • 滑动窗口的每段的最大值,容易想到“最大堆、最小堆”来解决,但是解决不了窗口滑动后移除元素的问题
  • 单调队列:递增、递减顺序的队列,所有元素按某一顺序存储
  • 此题中队列中的元素:
    • 要么是最大值
    • 要么是递增顺序的一些列值,最大值在队首,次大值在队列第二个位置。。。
  • 队列中存放元素值还是存放下标???
    • 滑动窗口,每次需要将最前面的元素移除,若存放元素值,则无法做到这一点
复杂度
  • 时间O(n)
  • 空间O(n)
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int[] res = new int[nums.length - k + 1];
        int idx = 0 ;
        LinkedList<Integer> deque = new LinkedList<>();
        for (int i = 0; i < nums.length; i++) {
            // 1.队列头结点需要在[i - k + 1, i]范围内,不符合则要弹出
            while(!deque.isEmpty() && deque.peek() < i - k + 1){
                deque.poll();
            }
            // 2.既然是单调,就要保证每次放进去的数字要比末尾的都大,否则也弹出
            while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }

            deque.offer(i);

            // 因为单调,当i增长到符合第一个k范围的时候,每滑动一步都将队列头节点放入结果就行了
            if(i >= k - 1){
                res[idx++] = nums[deque.peek()];
            }
        }
        return res ;
    }
}

第二次编码

  • 维护一个最大队列(队列首部是最大值)
  • 队列尾部和当前值比较,小于当前值得尾部的值都要弹出,保证队列顺序
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int[] res = new int[nums.length-k+1];
        int pos = 0 ;
        Deque<Integer> que = new LinkedList<>();
        for (int i = 0; i < nums.length; i++) {
            //队列中下标范围,i-k+1 ,i ,保证队列长度不会超过k,队列升序,可以把对首弹出
            while(!que.isEmpty() && que.peek() < i-k+1){
                que.poll();
            }
            // 保证对首永远是所有元素中的最大值
            while(!que.isEmpty() && nums[que.peekLast()] < nums[i]){
                que.pollLast();
            }
            //以上两步执行后,会将小于当前值得元素下标移除队列,保证队列得升序
            que.offer(i);

            if(i>= k-1){
                res[pos++] = nums[que.peek()];
            }
        }
        return res;
    }
}

第三次编码

public int[] maxSlidingWindow(int[] nums, int k) {
        int[] res = new int[nums.length - k +1];
        int pos = 0 ;
        //存储数组中元素的下标
        Deque<Integer> que = new LinkedList<>();
        for (int i = 0; i < nums.length; i++) {
            //控制下标范围
            while(!que.isEmpty() && que.peek() < i-k+1){
                que.poll();
            }
            //控制要入队的数据比对位的要大,移除队尾较小的值
            while(!que.isEmpty() && nums[que.peekLast()] < nums[i]){
                que.pollLast();
            }
            que.offer(i);

            if(i>=k-1){
                //够k位,即队列元素个数大于等于k
                res[pos++] = nums[que.peek()];
            }
        }
        return res ;
    }

347.前 K 个高频元素

  1. 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:

输入: nums = [1], k = 1
输出: [1]
思考
  • 前N个元素或者高频出现的元素容易想到优先队列,也成为了最大堆、最小堆
  • 用哪个?
    • 此题要求返回频次高的k个元素,换句话说,要把频次低的元素从队列中移除,而移除元素只能从堆顶移除,故需要使用最小堆
  • 最小堆的顺序使用哪个?简单的存放元素值吗?
    • 每次移除的元素一定是频次低的元素,所以需要根据存放元素出现的频次,使其自平衡
  • 队列中存放的元素即为频率最高的元素集合
复杂度
  • 时间 O(nlongn)
  • 空间 O(n)
import java.util.HashMap;
import java.util.PriorityQueue;

//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        PriorityQueue<Integer> que = new PriorityQueue<Integer>((a,b)->map.get(a)-map.get(b));
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        for (int v:map.keySet()) {
            if(que.size()<k){
                que.offer(v);
            }else{
                if(map.get(que.peek()) < map.get(v)){
                    que.poll();
                    que.offer(v);
                }
            }
        }
        int[] res = new int[k];
        int idx = 0 ;
        while (!que.isEmpty()) {
            res[idx++] = que.poll();
        }
        return res ;
    }
}

总结

  • 栈和队列的题,重点在于构造队列或栈,大多数题需要把每次计算的结果重新放入容器内,参与下次计算

  • 单调栈、单调队列:元素按某一顺序存放

  • 最大堆、最小堆:分析每次需要移除的元素,确定用哪个,在构造函数中指定顺序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值