算法打卡day11|栈与队列篇03|Leetcode 239. 滑动窗口最大值、347.前 K 个高频元素

本文主要是介绍算法打卡day11|栈与队列篇03|Leetcode 239. 滑动窗口最大值、347.前 K 个高频元素,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

小顶堆和大顶堆

小顶堆(Min Heap)和大顶堆(Max Heap)是两种特殊的完全二叉树,它们遵循特定的堆属性,即父节点的值总是小于或等于(小顶堆)或者大于或等于(大顶堆)其子节点的值。这种结构使得堆的根节点包含了最大值或者最小值,这使得堆在很多算法中非常有用,尤其是在优先级队列的实现上。

小顶堆(Min Heap)
在小顶堆中,每个非叶子节点的值都必须大于或等于其子节点的值。这意味着堆的根节点包含了所有节点中的最小值。小顶堆通常用于那些需要快速访问最小元素的场景比如实现优先队列也就是今天的力扣347题

大顶堆(Max Heap)
在大顶堆中,每个非叶子节点的值都必须小于或等于其子节点的值。因此,堆的根节点包含了所有节点中的最大值。大顶堆通常用于需要快速访问最大元素的场景

堆的操作
堆提供了几种基本操作:

  • insert(或add):向堆中添加一个新元素。
  • remove:移除堆中的最大元素(在大顶堆中)或最小元素(在小顶堆中)。
  • heapify:将一个数组转换成堆结构。
  • extractMax / extractMin:从堆中提取并移除最大元素(在大顶堆中)或最小元素(在小顶堆中)。

在Java中实现堆,Java标准库中的PriorityQueue就是基于堆实现的,默认情况下它使用小顶堆。可以指定一个Comparator来改变优先级队列的比较规则,从而可以实现大顶堆。

算法题

Leetcode  239. 滑动窗口最大值

题目链接:239. 滑动窗口最大值

大佬视频讲解:滑动窗口最大值视频讲解

个人思路

思路打结,不知道怎么实现

解法
单调队列

使用单调递减队列,将放进去窗口里的元素随着窗口的移动,队列也一进一出,每次移动之后,队列直接知道最大值是什么。

还有注意,队列没有必要维护窗口里的所有元素只需要维护有可能成为窗口里最大值的元素,同时保证队列里的元素数值是由大到小的。

设计单调队列的时候,pop,和push操作要保持如下规则:

  1. poll(val):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  2. add(val):如果add的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到add元素的数值小于等于队列入口元素的数值为止

保持如上规则,每次窗口移动的时候,只要deque.peek()就可以返回当前窗口的最大值。

class MyQueue {Deque<Integer> deque = new LinkedList<>();//弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出;void poll(int val) {if (!deque.isEmpty() && val == deque.peek()) { //也要判断队列当前是否为空deque.poll();}}//添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出,这样保证队列元素单调递减//比如此时队列元素4,3,1 ;5将要入队,比4,3,1都大,所以都弹出,此时队列:5void add(int val) {while (!deque.isEmpty() && val > deque.getLast()) {//push元素的数值小于等于队列入口元素的数值为止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;MyQueue myQueue = new MyQueue();//自定义队列//先将前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;}
}

时间复杂度:O(n);(遍历数组)

空间复杂度:O(n);(辅助数组和队列)

Leetcode 347.前 K 个高频元素

题目链接:347.前 K 个高频元素

大佬视频讲解:前 K 个高频元素 视频讲解

个人思路

根据题意解答分为三步走,第一步要统计元素出现频率,第二步对频率排序,第三步找出前K个高频元素

解法
小顶堆

第一步统计元素出现的频率,这一类的问题可以使用map来进行统计。

第二步对频率进行排序,可以使用一种 容器适配器就是优先级队列,就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。

第三步找出前k个高频元素因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

如下图所示

class Solution {public int[] topKFrequent(int[] nums, int k) {Map<Integer,Integer> map = new HashMap<>();//key为数组元素值,val为对应出现次数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]){//当前元素出现次数大于小顶堆的根结点//弹出队头(小顶堆的根结点),即把堆里出现次数最少的那个删除,留下的就是出现次数多的了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;}
}

时间复杂度:O(nlogk);(小顶堆构建)

空间复杂度:O(n);(优先队列,结果数组都不超过n)

以上是个人的思考反思与总结,若只想根据系列题刷,参考卡哥的网址代码随想录算法官网代码随想录算法官网代码随想录算法官网

这篇关于算法打卡day11|栈与队列篇03|Leetcode 239. 滑动窗口最大值、347.前 K 个高频元素的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/786679

相关文章

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

C++从序列容器中删除元素的四种方法

《C++从序列容器中删除元素的四种方法》删除元素的方法在序列容器和关联容器之间是非常不同的,在序列容器中,vector和string是最常用的,但这里也会介绍deque和list以供全面了解,尽管在一... 目录一、简介二、移除给定位置的元素三、移除与某个值相等的元素3.1、序列容器vector、deque

C++常见容器获取头元素的方法大全

《C++常见容器获取头元素的方法大全》在C++编程中,容器是存储和管理数据集合的重要工具,不同的容器提供了不同的接口来访问和操作其中的元素,获取容器的头元素(即第一个元素)是常见的操作之一,本文将详细... 目录一、std::vector二、std::list三、std::deque四、std::forwa

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

Spring Boot整合消息队列RabbitMQ的实现示例

《SpringBoot整合消息队列RabbitMQ的实现示例》本文主要介绍了SpringBoot整合消息队列RabbitMQ的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录RabbitMQ 简介与安装1. RabbitMQ 简介2. RabbitMQ 安装Spring

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Python使用Pandas对比两列数据取最大值的五种方法

《Python使用Pandas对比两列数据取最大值的五种方法》本文主要介绍使用Pandas对比两列数据取最大值的五种方法,包括使用max方法、apply方法结合lambda函数、函数、clip方法、w... 目录引言一、使用max方法二、使用apply方法结合lambda函数三、使用np.maximum函数