代码随想录算法训练营第十三天|239.滑动窗口最大值,前K个高频元素,总结

239.滑动窗口最大值

力扣

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

返回滑动窗口中的最大值。

进阶:

你能在线性时间复杂度内解决此题吗?

解题思路:

  • 直接想到的就是暴力循环解题。动态队列维持k个元素,出一个,进一个。每次都对队列中的k个元素进行遍历寻找最大值。(超出时间,时间复杂度为n*k)
  • 采用单调队列的方法。让队列保持单调递减。则最大值永远放在队口。每次入队时都做判断,前面的元素小于新添加的元素则直接弹出RemoveLast,直到弹完或者存在元素比新元素大放在其后。每次出队时也都做判断,每次循环都按照顺序弹出,但是存在入队时已经将此元素弹出的情况,则只弹出和当前顺序相等的元素。

解法:

public class Solution {
        List<int> l1 = new List<int>();
        public MyQue myQ = new MyQue();

    public int[] MaxSlidingWindow(int[] nums, int k) {
        for(int i = 0;i < k;i++){
            myQ.Enqueue(nums[i]);
        }
        l1.Add(myQ.GetMax());

        for(int j = k;j < nums.Length;j++){
            myQ.Dequeue(nums[j-k]); //开始按顺序弹出
            myQ.Enqueue(nums[j]);
            l1.Add(myQ.GetMax());
        }
        return l1.ToArray();
    }
}

public class MyQue{
    public LinkedList<int> ll1 = new LinkedList<int>();

    public void Enqueue(int n){ //每次都入队列,但是要保持队列单调递减
        while(ll1.Count >0 && ll1.Last.Value < n){
            ll1.RemoveLast();
        }
        ll1.AddLast(n);
    }

    public void Dequeue(int n){ //按顺序弹出,表示每次都会弹出,如果没弹则表示入队列的时候已经弹出
        if(ll1.First.Value == n){
            ll1.RemoveFirst();
        }
    }

    public int GetMax(){
        return ll1.First.Value;
    }
}

时间复杂度:O(n)

空间复杂度:O(k)

347.前K个高频元素

力扣

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

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

示例 2:

  • 输入: nums = [1], k = 1
  • 输出: [1]

提示:

  • 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 $O(n \log n)$ , n 是数组的大小。
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
  • 你可以按任意顺序返回答案。

解题思路:

  • 想到了使用map来进行分堆,但是对于排序查找,没有想到很好的方法。
  • 这里排序使用了自带的数据结构PriorityQueue。

PriorityQueue介绍:

  1. 计算机系统常遇到这样一类问题:前一个任务已经执行完成,需要在待执行任务中挑选一个新的执行。最简单的方法是按照队列先入先出FIFO来进行。但是会导致紧急度高的任务在队列中等待时间过长。为了解决这个问题,有了这个策略:优先级队列。
  2. 优先级队列出队顺序总是按照元素自身的优先级。
  3. c#中使用void Enqueue(TElement element, TPriority priority) 基于优先级添加元素。

解法:

public class Solution {
    public int[] TopKFrequent(int[] nums, int k) {
        Dictionary<int,int> dic = new();
        for(int i = 0; i < nums.Length; i++){
            if(dic.ContainsKey(nums[i])){
                dic[nums[i]]++;
            }else{
                dic.Add(nums[i], 1);
            }
        }
        //优先队列-从小到大排列
        PriorityQueue<int,int> pq = new();
        foreach(var num in dic){
            pq.Enqueue(num.Key, num.Value);
            if(pq.Count > k){
                pq.Dequeue();
            }
        }

         int[] res = new int[k];
        for(int i = k - 1; i >= 0; i--){
            res[i] = pq.Dequeue();
        }
        return res;
    }
}

总结:

  • 在栈与队列中,有两种特殊队列。1.单调队列。2.优先级队列。
  • 大顶堆与小顶堆的概念。堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值