代码随想录算法训练营第十二天|LeetCode239,347

代码随想录算法训练营第十二天|LeetCode239,347

239. 滑动窗口最大值

主要思想是队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列为单调队列。
设计单调队列的时候,pop,和push操作要保持如下规则:

  • poll(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  • add(value):如果push的元素add大于入口元素的数值,那么就将队列出口的元素弹出,直到add元素的数值小于等于队列入口元素的数值为止
    保持如上规则,每次窗口移动的时候,只要问que.peek()就可以返回当前窗口的最大值。

其次要明确的是,题解中单调队列里的pop和push接口,仅适用于本题。

 class MyDeque{
        //使用Deque来实现单调序列
        ArrayDeque<Integer> que = new ArrayDeque<>();
        //每次弹出的时候,比较当前弹出的数值是否等于队列出口处的元素
        //记得弹出的时候判断队列是不是为空
        void poll(int val){
            if (!que.isEmpty()&&val == que.peek()){
                que.poll();
            }
        }

        //添加元素的时候,如果要添加的元素大于入口处的元素,就将入口处的元素弹出
        //保证单调队列单调递减
        //比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
        void add(int val){
            while (!que.isEmpty()&&val>que.getLast()){
                que.removeLast();
            }
            que.add(val);
        }

        int peek(){
            return que.peek();
        }

    }

    public  int[] maxSlidingWindow(int[] nums, int k) {

        if (nums.length == 1){
            return nums;
        }

       // ArrayList<Integer> res = new ArrayList<>();
        //计算最后结果数组的长度,可以用ArrayList
        int len = nums.length - k + 1;
        int[] res = new int[len];
        //结果数组的下标
        int num = 0;
        //自定义队列
        MyDeque myQue = new MyDeque();
        //先将前K个元素放入自定义队列
        for (int i = 0; i< k;i++){
            myQue.add(nums[i]);
        }
        res[num++] = myQue.peek();
        for (int i = k;i<nums.length; i++){
            //滑动窗口移除最前面的元素,同时判断是否需要将队列中的第一个弹出
            myQue.poll(nums[i-k]);
            //将滑动窗口的最后一个元素加入单调队列
            myQue.add(nums[i]);
            //记录对应的最大值
            res[num++] = myQue.peek();

        }
        return res;
    }

347. 前 K 个高频元素

遍历数组,然后新建HashMap来记录每个值出现的频率,然后排序返回题目要求的前K个最大值
比较难实现的地方在于,排序具体怎么实现。参考题解对频率进行排序可以使用一种优先级队列(底层实现为堆)。
如果使用大顶堆的话,就需要将所有元素进行排序
,那能不能只排序k个元素呢?
定义一个大小为k的大顶堆,在每次移动更新大顶堆的时候,每次弹出都把最大的元素弹出去了,那么怎么保留下来前K个高频元素呢。
参考题解用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

 public int[] topKFrequent(int[] nums, int k) {
        //Key为数组元素值,val为该元素出现的次数
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int num:nums){
            map.put(num, map.getOrDefault(num,0)+1);
        }

        //在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
        //出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(小顶堆)
        PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);

        for (Map.Entry<Integer,Integer> entry:map.entrySet()){//小顶堆维持k个元素有序
            if (pq.size()<k){//小顶堆元素个数小于k时直接加
                pq.add(new int[]{entry.getKey(), entry.getValue()});
            }else {
                if (entry.getValue()>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;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值