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介绍:
- 计算机系统常遇到这样一类问题:前一个任务已经执行完成,需要在待执行任务中挑选一个新的执行。最简单的方法是按照队列先入先出FIFO来进行。但是会导致紧急度高的任务在队列中等待时间过长。为了解决这个问题,有了这个策略:优先级队列。
- 优先级队列出队顺序总是按照元素自身的优先级。
- 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.优先级队列。
- 大顶堆与小顶堆的概念。堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。