代码随想录训练营day13:滑动窗口最大值,前k个高频元素-创新互联

滑动窗口大值

创新互联公司主要从事成都做网站、成都网站设计、成都外贸网站建设、网页设计、企业做网站、公司建网站等业务。立足成都服务万山,10年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:18980820575

这是做过的第一个困难题目, 虽然看起来不是很难, 但是有一些细节要注意

总体思路: 队列存的是下标, 并且数组中的数要从大到小排序

有三个重要点: 

  1. while循环判断, 如果nums[deque.peekLast]小于val, 那么就要removeLast, 同时把val加上

  2. 如果deque.peekFirst在nums[i-k]窗口之外了, 就要removeFirst

  3. 如果i ≥ k- 1也就是窗口被填满了, 那么就开始存max

细节:
1.首先需要一个队列, 一个数组, 一个默认值inde, 队列用来保存下标, 数组保存大值, index用来给数组计数

2.关键就是让这个窗口移动, 向右移要判断之后pollLast, 然后offerLast

3. 首先用一个while, 然后窗口的pollFirst和添加数组用if就行了

4.记住这里最后的数组长度就是nums.length - k + 1, 也就是滑动窗口的个数

5. i - k + 1代表着这个窗口左侧, 需要判断队列的first在不在窗口内这里要包括等于号啊

6.最后的判断别忘了边界处理大于等于窗口大小

总结,滑动窗口三步走

  1. 判断如何在右边添加数字, 判断如果小了就poll掉
  2. 判断如何在左边边界移动, 判断如果小于窗口就去掉
  3. 判断是否填满窗口, 满足就return peek
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        //存下标可以更方便的来确定元素是否需要移出滑动窗口
        //判断下标是否合法来确定是否要移出
        Dequeq = new LinkedList<>();
        //res就是最后的长度, 也就是总共窗口的长度
        int[] res = new int[nums.length - k + 1] ;
        int index = 0;
        for(int i = 0; i< nums.length; i++){
            //保证队列的单调递减,使队列的出口始终为大值(窗口右移)
            //注意队列存的是数组下标,所以判断逻辑是nums[i] >nums[q.peekLast()]
            //容易误写成nums[i] >q.peekLast()
            while(!q.isEmpty() && nums[i] >nums[q.peekLast()]){
                q.pollLast();
            }
            q.offerLast(i);
            //判断队列出口的值是否合法,如果值的下标不在窗口内则要将其移出
            if(q.peek()<= i - k){
                q.pollFirst();
            }
            //窗口至少填满一次后才开始放大值
            //依然要注意队列存的是下标,所以赋值是赋nums[q.peekFirst()]
            if(i >= k - 1){
                res[index++] = nums[q.peek()];
            }            
        }
        return res;
    } 
}

前k个高频元素

这道题思路其实不难, 但是设计很多基础的数据结构知识, 我都不熟悉, 导致做得很折磨

思路(大顶堆): 复杂度nLogn

  1. 先遍历数组, 用map收集key和value
  2. 然后把map的元素放进优先队列
  3. 最后创建一个新的数组, 遍历前k个数据就行了

细节:

1. 创建优先队列涉及到lambda表达式和comparator

2.最后遍历数据没有理解结构pq.poll()[0]是什么意思

这里先上代码, 需要把排序和集合巩固之和才能完全理解, lambda和comparator如下 

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
				//先把数组的key和value放到哈希表里面
        HashMaphash = new HashMap<>();
        for(int num : nums){
            if(hash.containsKey(num)){
                hash.put(num, hash.get(num) + 1);
            } else{
                hash.put(num, 1);
            }
        }
				//然后把哈希表的数字放到优先队列里, 这里涉及到lambda表达式
        PriorityQueuepq = new PriorityQueue<>((pair1, pair2) ->pair2[1] - pair1[1]);
        for(Map.Entryentry : hash.entrySet()){
            pq.add(new int[]{entry.getKey(), entry.getValue()});
        }
				//已经自动排好序了, 直接创建数组导出来就行了
        int[] ans = new int[k];
        for(int i = 0; i< k; i++){
            ans[i] = pq.poll()[0];
        }
        return ans;
    }
}

小顶堆: 复杂度nLogk

由于时间关系暂时不搞了, 周末再来看

public int[] topKFrequent2(int[] nums, int k) {
        Mapmap = new HashMap<>();//key为数组元素值,val为对应出现次数
        for(int num:nums){
            map.put(num,map.getOrDefault(num,0)+1);
        }
        //在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
        //出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(相当于小顶堆)
        PriorityQueuepq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);
        for(Map.Entryentry:map.entrySet()){//小顶堆只需要维持k个元素有序
            if(pq.size()pq.peek()[1]){//当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)
                    pq.poll();//弹出队头(小顶堆的根结点),即把堆里出现次数最少的那个删除,留下的就是出现次数多的了
                    pq.add(new int[]{entry.getKey(),entry.getValue()});
                }
            }
        }
        int[] ans = new int[k];
        for(int i=k-1;i>=0;i--){//依次弹出小顶堆,先弹出的是堆的根,出现次数少,后面弹出的出现次数多
            ans[i] = pq.poll()[0];
        }
        return ans;
    }

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享名称:代码随想录训练营day13:滑动窗口最大值,前k个高频元素-创新互联
转载来于:http://ybzwz.com/article/jhgse.html