LeetCode:347.前K个高频元素 && 239.滑动窗口最大值

347.前K个高频元素

题目

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
LeetCode:347.前K个高频元素 && 239.滑动窗口最大值_第1张图片
来源:力扣(LeetCode)
链接:前K个高频元素

小顶堆

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

思路

  • 要统计元素出现频率(map)
  • 对频率排序(PriorityQueue优先队列,小顶堆)
    • 使用小顶堆是为了将出现次数最少的数值弹出,就可以保留出现次数最多的数值
  • 找出前K个高频元素
    LeetCode:347.前K个高频元素 && 239.滑动窗口最大值_第2张图片
class Solution {
    //基于小顶堆的实现
    public int[] topKFrequent(int[] nums, int k) {
        //使用Map集合对nums数组中的元素进行统计出现的次数
        Map<Integer, Integer> map = new HashMap<>();
        for(int num : nums){
            /*
            map.getOrDefault(num, 0):
            当数组不存在map集合的num(key值)时,就将其对应的0(value值)输出
            如果存在则输出在集合中key所对应的value值
            */
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        //在优先队列中存储二元组(num, cnt),cnt是num在数组中出现的次数
        //出现的次数按照队头到队尾从小到大排列,最小的数出现队头(小顶堆)
        PriorityQueue<int[]> pq = new PriorityQueue<>((p1, p2) -> p1[1] - p2[1]);
        //小顶堆只需要维持k个元素有序,无需将整个pq全部排序
        //Map.Entry:映射键值对
        for(Map.Entry<Integer, Integer> entry : map.entrySet()){
            //小顶堆的个数 < k时进行添加
            if(pq.size() < k){
                //将key, value添加到pd中
                pq.add(new int[]{entry.getKey(), entry.getValue()});
            }else{
                //当前元素出现的次数 > 小顶堆pq的根节点
                if(entry.getValue() > pq.peek()[1]){
                    //弹出队头,把最小的弹出去,大的就会被留下来
                    pq.poll();
                    pq.add(new int[]{entry.getKey(), entry.getValue()});
                }
            }
        }
        int[] res = new int[k];
        //倒序输出
        /*
            因为是小顶堆,所以出现次数少的会被排在根节点
            那么按照正序输出的话,就是现下出现次数最少的先输出
        */
        for(int i = k - 1; i >= 0; i--){
            res[i] = pq.poll()[0];
        }
        return res;
    }
}

大顶堆

思路

LeetCode:347.前K个高频元素 && 239.滑动窗口最大值_第3张图片

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        //使用Map集合对nums数组中的元素进行统计出现的次数
        Map<Integer, Integer> map = new HashMap<>();
        for(int num : nums){
            /*
            map.getOrDefault(num, 0):
            当数组不存在map集合的num(key值)时,就将其对应的0(value值)输出
            如果存在则输出在集合中key所对应的value值
            */
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        //在优先队列中存储二元组(num, cnt),cnt是num在数组中出现的次数
        //出现的次数按照队头到队尾从大到小排列,最大的数出现队头(大顶堆)
        PriorityQueue<int[]> pq = new PriorityQueue<>((p1, p2) -> p2[1] - p1[1]);
        // 大顶堆排序
        //Map.Entry:映射键值对
        for(Map.Entry<Integer, Integer> entry : map.entrySet()){
            //将key, value添加到pd中
            pq.add(new int[]{entry.getKey(), entry.getValue()});
        }
        // 正序取出,因为出现次数最多的元素已经通过大顶堆排到了前面
        int[] res = new int[k];
        for(int i = 0; i < k; i++){
            res[i] = pq.poll()[0];
        }
        return res;
    }
}

239.滑动窗口最大值

题目

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
LeetCode:347.前K个高频元素 && 239.滑动窗口最大值_第4张图片
来源:力扣(LeetCode)
链接:滑动窗口最大值

双端队列

用一个单调队列来存储对应的下标,每当窗口滑动的时候,直接取队列的头部指针对应的值放入结果集即可

  • 时间复杂度:O(n)
  • 空间复杂度:O(k),k是辅助队列
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        //创建双端队列
        ArrayDeque<Integer> deque = new ArrayDeque<>();
        int n = nums.length;
        //res数组是用来存放比较后最大的数值
        int[] res = new int[n - k + 1];
        //res数组的下标
        int x = 0;
        for(int i = 0; i < n; i++){
            //判断deque是否为空,并且deque队头元素值是否小于i - k + 1
            while(!deque.isEmpty() && deque.peek() < i - k + 1){
                //获取队头元素,但不删除元素
                deque.poll();
            }
            //判断deque是否为空,并且deque队尾元素值是否小于nums[i]值
            while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]){
                //获取队尾元素,但不删除元素
                deque.pollLast();
            }
            //在deque的队尾加入指定的元素
            deque.offer(i);
            //i >= k - 1:假设k = 3,那么当i的值 > 2的时候,证明比较完后最大的数值的个数已经超过了k的范围,所以每增加一个数值,就将前一个数值存入到res数组中
            if(i >= k - 1){
                //在res数组中加入对比完后最大的数值
                res[x++] = nums[deque.peek()];
            }
        }
        return res;
    }
}

自定义队列

  • 时间复杂度: O(n)
  • 空间复杂度: O(k)
class MyQeque{
    Deque<Integer> deque = new LinkedList<>();
    // 弹出元素,判断弹出的元素与队列出口的元素是否相等,是则一次弹出
    void poll(int val){
        if(!deque.isEmpty() && val == deque.peek()){
            deque.poll();
        }
    }
    // 添加元素时,比较队列中末尾元素的大小,添加的元素大于队列末尾的元素,则将末尾元素移除
    void add(int val){
        while(!deque.isEmpty() && val > deque.getLast()){
            deque.removeLast();
        } 
        deque.add(val);
    }
    // 队列顶部的元素始终为最大值
    int peek(){
        return deque.peek();
    }
}
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 1) return nums;
        int len = nums.length - k + 1;
        int[] res = new int[len];
        int num = 0;
        // 自定义队列
        MyQeque myQueue = new MyQeque();
        // 先将数组中前k个元素添加到队列中
        for(int i = 0; i < k; i++){
            myQueue.add(nums[i]);
        }
        res[num++] = myQueue.peek();
        for(int i = k; i < nums.length; i++){
            // 在滑动窗口中移除最前面的元素
            myQueue.poll(nums[i - k]);
            // 滑动窗口添加最后面的元素
            myQueue.add(nums[i]);
            // 记录对应的最大值
            res[num++] = myQueue.peek();
        }
        return res;
    }
}

你可能感兴趣的:(LeetCode,leetcode,算法,java,队列)