day11 | 栈与队列 part-3 (Go) | 239 滑动窗口最大值、347 前 K 个高频元素 (好难)

今日任务 

  • 239 滑动窗口最大值 (题目:. - 力扣(LeetCode) )
  • 347 前 K 个高频元素 (题目: . - 力扣(LeetCode) )
  • 栈与队列总结

239 滑动窗口最大值 

      题目:. - 力扣(LeetCode) 

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

想法:

        看到滑动窗口,窗口是一进一出的,能想到是需要一个队列,队列的长度应该是k.  返回的最终结果数组的长度应该是 len(nums)-(k-1).但是如何求每个窗口(队列)中的最大值是多少??  当时想的对每个窗口排序,也未实现. 

问题:

        认真的说, 我看了好多视频和文字题解都没理解他们说的: “队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。”   例:对于窗口里的元素{2, 3, 5, 1 ,4},单调队列里只维护{5, 4} 就够了,保持单调队列里单调递减,此时队列出口元素就是窗口里最大元素。

        其实上面真正困惑我的是一直把“滑动窗口”这个题目和单调队列的实现效果,混在一起思考了,因为我不明白的是我就算是把(窗口)队列的元素 从“{2, 3, 5, 1 ,4}”变成了“{5, 4} ”, 窗口的长度都变了,我窗口咋移除最前面的元素.........直到我仔细看了两遍代码 

        (窗口的大小并没有变,仍是 k , 只是窗口所对应的队列,是我们经过处理的,例如:我窗口是{2,3,5}和向后移动两步的窗口{5,1,4} 其中最大值都是 5. 那么对于这2 步移动过程中 单调队列的“出口处”一直都是{5}. 那移动过程中,判断窗口左侧的值 nums[L]和队列的出口处的值是否相等,不相等就不用有移除的操作了,因为在单调队列中 ,加入元素 5 的时候,前面 2 和 3 就不可能再成为窗口最大的元素了,已经移除过了......不知道这样解释能看明白嘛)   这段话是我在看了代码之后才产生的理解.

        其实理解过一次之后,这个单调队列就特别清晰了

解决思路:

        首先呢, 我们要明确我们这道题的关键是,如何求得每个窗口(队列)中的最大值是多少?  

        此时我们需要一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么。实现一个单调递减队列: 调用que.pop(滑动窗口中移除元素的数值),que.push(滑动窗口添加元素的数值),然后que.front()就返回我们要的最大值。

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

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

保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。队列的具体实现见下面代码:

题解和图片参考自: 代码随想录

// 定义一个结果切片, 还有就是 结果切片的长度 = len(nums) -(k-1)
// 用一个切片模拟队列, 队列里的长度保持为 k,  现在好奇的是如何判断队列里面的元素最大呢?
// (需要一个单调递减队列)
// ------------------------------------
// 自己理清思路后,写出的代码
// 定义一个队列,单调递减的队列.,下面是一些实现单调队列的方法
type MyQueue struct {queue []int
}func NewMyQueue() *MyQueue {return &MyQueue{queue: make([]int,0),}
}// 获取队列的出口值(获取窗口最大值),经过特殊处理的队列,出口值肯定是当前窗口中最大的
func (m *MyQueue) Front() int {return m.queue[0]
}// 获取队列的入口值,即数组的最后
func (m *MyQueue) Back() int {return m.queue[len(m.queue)-1]
}// 队列是否为空
func (m *MyQueue) Empty() bool {return len(m.queue) == 0
}// 加入元素 push, 如果要加入的元素比队列入口的值大,则将队列入口的值弹出,直到碰到比自己大的,或者队列为空时,将元素加入队列.
func (m *MyQueue) Push(val int) {for !m.Empty() && m.Back() < val {m.queue = m.queue[:len(m.queue)-1]}m.queue = append(m.queue,val)
}// 弹出元素 pop , 如果队列的出口处 和 val 相等,则将该元素从队列中弹出(val 是窗口向后移动过程中,要移除的元素)
func (m *MyQueue) Pop(val int) {if !m.Empty() && m.Front() == val{m.queue = m.queue[1:]}
}// -----------------------------------
func maxSlidingWindow(nums []int,k int) []int {queue := NewMyQueue()length := len(nums)res := make([]int,0)// 先将 k 个元素放入队列for i:=0;i<k;i++{queue.Push(nums[i])}// 先将第一个窗口的最大值加入res = append(res,queue.Front())// 移动窗口for i := k; i<length;i++{// 将窗口新加入的元素 添加到 队列中queue.Push(nums[i])// 将窗口后移,前面溢出的元素从队列中移除(如果元素还在队列中的话)queue.Pop(nums[i-k])// 将当前窗口中最大的元素加入到 res 中.res = append(res,queue.Front())}return res
}

 347 前 K 个高频元素

      题目: . - 力扣(LeetCode) 

      给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

      进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

      示例-输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]

想法:

        第一反应: 循环统计每个频率高的元素,然后按照其出现的次数多少来排序,然后输出切片的前 k 位, 暂时没想到和 栈、队列 能产生什么关系啊

问题:

        这题目猛一看思路很简单: (1)要统计元素出现频率 (2) 对频率排序 (3) 找出前K个高频元素

然而 对频率如何排序 这个问题是有点棘手, 常规想到的就是 排序的一些包直接排一下, 然而看到的题解大部分都在说使用小顶堆,然而我还不知道什么是小顶堆.....😭

科普:

        堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。

        我们要用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

        寻找前k个最大元素流程如图所示:(图中的频率只有三个,所以正好构成一个大小为3的小顶堆,如果频率更多一些,则用这个小顶堆进行扫描)

        题解和图片参考自: 代码随想录        

解决思路:

        (1) 首先使用 hash map 记录每个元素出现的次数

        (2) 定义一个小顶堆,用来排序map的 value. 其实要借助 go 中的 container/heap 的包. 我们要自己实现一些堆的接口(具体效果是小顶堆还是大顶堆,和自己实现的接口方法有关.) 我也尚未仔细研究这个引用的包,也不能很好的讲清楚这里.待我研究明白后,看能不能补齐一个完整的小顶堆的实现.... 

        (3) 从小顶堆中取出高频元素并返回, 对于小顶堆来说 pop 出来的都是从小到大的元素,这里注意 我们可以往结果数组中 倒着塞入数据即可.

注: 这题从难度上来说好像没有滑动窗口难, 但是这题让我理解起来确实很困难, 因为我不理解那些题解上来就讲 “优先队列、小顶堆、大顶堆、然后最后还得是借助的封装好的包来将元素入堆”, 搞得很懵, 希望二刷时,我能带着更丰富的知识来解这种题.

// 方法一: 使用小顶堆排序
func topKFrequent(nums []int, k int) []int {map_num := map[int]int{}//记录每个元素出现的次数for _, item := range nums {map_num[item]++}h := &IHeap{}heap.Init(h)//所有元素入堆,堆的长度为kfor key, value := range map_num {heap.Push(h, [2]int{key, value})if h.Len() > k {heap.Pop(h)}}res := make([]int, k)//按顺序返回堆中的元素, 其实小顶堆取出来的元素都是从小到大的,那么在往 res 数组中塞数据时,应该倒着往数组里添加.for i := 0; i < k; i++ {res[k-i-1] = heap.Pop(h).([2]int)[0]}return res
}// 构建小顶堆
type IHeap [][2]intfunc (h IHeap) Len() int {return len(h)
}func (h IHeap) Less(i, j int) bool {return h[i][1] < h[j][1]
}func (h IHeap) Swap(i, j int) {h[i], h[j] = h[j], h[i]
}func (h *IHeap) Push(x interface{}) {*h = append(*h, x.([2]int))
}
func (h *IHeap) Pop() interface{} {old := *hn := len(old)x := old[n-1]*h = old[0 : n-1]return x
}//方法二: 利用O(nlogn)排序
func topKFrequent(nums []int, k int) []int {ans:=[]int{}map_num:=map[int]int{}for _,item:=range nums {map_num[item]++}for key,_:=range map_num{ans=append(ans,key)}//核心思想:排序//可以不用包函数,自己实现快排sort.Slice(ans,func (a,b int)bool{return map_num[ans[a]]>map_num[ans[b]]})return ans[:k]
}

栈与队列总结

        嘿嘿,偷个懒,参考卡哥的: 代码随想录 ( 这也不是打广告啥的吧,就单纯觉得人家的东西好, 省时省力吧)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/823962.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

处理json文件,并将数据汇总至Excel表格

从scores.jason文件中读取学生信息,输出学生的学号&#xff0c;姓名&#xff0c;各科成绩&#xff0c;平均分, 各科标准差 scores.jason {"学院": "计算机学院","班级": "2022级1班","成绩": [{"学号": 1001,&q…

使用avx2 指令集加速向量算法运算

使用cpu-z 查看cpu指令集 2 向量加&#xff0c;乘法&#xff0c;除法 我们使用向量加&#xff0c;为什么函数是0 到 8 的计算&#xff0c;因为avx2 寄存器为256位&#xff0c;同时设置启动增强指令集 #include <immintrin.h> // 引入包含AVX2指令集的头文件void vecto…

C++类和对象:赋值重载,const成员

文章目录 1.赋值运算符重载1.1运算符重载1.2 赋值运算符重载1.3 前置和后置重载 2.日期类的实现3. const成员函数4 取地址及const取地址操作符重载 上文介绍了前三个默认成员函数&#xff0c;本文会介绍剩下三个&#xff0c; 赋值重载会重点展开。 1.赋值运算符重载 1.1运算符…

代码随想录 Day17 字符串 | LC344 反转字符串 LC541 反转字符串II 卡码网54替换数字

一、反转字符串 题目&#xff1a; 力扣344&#xff1a;反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题…

微服务相关

1. 微服务主要七个模块 中央管理平台&#xff1a;生产者、消费者注册&#xff0c;服务发现&#xff0c;服务治理&#xff0c;调用关系生产者消费者权限管理流量管理自定义传输协议序列化反序列化 2. 中央管理平台 生产者A在中央管理平台注册后&#xff0c;中央管理平台会给他…

不说成为Linux高级工程师,但成为合格的软件开发人员还是够了,一文深入浅出的精炼总结Linux核心知识点,掌握Linux的使用与高阶技巧

不说成为Linux高级工程师&#xff0c;但成为合格的软件开发人员还是够了&#xff0c;一文深入浅出的精炼总结Linux核心知识点&#xff0c;掌握Linux的使用与高阶技巧。 Linux 的学习对于一个程序员的重要性是不言而喻的。前端开发相比后端开发&#xff0c;接触 Linux 机会相对…

图像基础—图像分类

图像通常分为二值图像、灰度图像和彩色图像 图 1-3 二值图像、灰度图像和彩色图像 &#xff08;1&#xff09;二值图像 二值图像又称为黑白图像&#xff0c;图像中任何一个点非黑即白&#xff0c;要么为白色&#xff08;像素 为 255&#xff09;&#xff0c;要么为黑色&#x…

20240417,友元 FRIEND

本来要学习的吃瓜吃了一下午 目录 3.1 全局函数做友元 3.2 友元类 3.3 成员函数做友元 三&#xff0c;友元 3.1 全局函数做友元 #include<iostream> using namespace std; class Building {friend void goodGay(Building* building);//好朋友&#xff0c;可以访问…

YooAsset快速入门

文章目录 YooAsset快速入门指南&#xff1a;YooAsset学习核心要掌握的要点主要包括以下几个方面&#xff1a;基于YooAsset进行游戏资源管理的应用实例 YooAsset快速入门指南&#xff1a; YooAsset是一款专为游戏开发设计的资产管理和分发系统&#xff0c;它能够帮助开发者高效…

2024阿里云4核8G服务器租用优惠价格700元一年

阿里云4核8G服务器租用优惠价格700元1年&#xff0c;配置为ECS通用算力型u1实例&#xff08;ecs.u1-c1m2.xlarge&#xff09;4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选&#xff0c;CPU采用Intel(R) Xeon(R) Platinum处理器&#xff0c;阿里云优惠 aliyunfuwuqi…

怎样将excel的科学计数法设置为指数形式?

对了&#xff0c;这个问题中所谓的“指数形式”是指数学上书写的右上标的指数格式&#xff0c;能不能通过单元格设置来做这个格式的转换呢&#xff1f; 一、几个尝试 以下&#xff0c;以数字123000为例来说明。 情况1.转换成数学上的书写方式&#xff0c;如下图的样子&#x…

Easy GIS .NET GMap.Net

Easy GIS .NET & GMap.Net .NET 环境下非常简单的GIS地图开发库。 Easy GIS .NET 一个简单的GIS 桌面应用程序&#xff0c;实现了地图瓦片加载、shapefile文件和csv文件加载渲染、地图坐标系统设置及转换等等基本功能&#xff0c;非常简单易用。 Easy GIS .NET is an o…

算法之归并排序(java、python、c++)

一、JAVA 摘录归并详细分析 ✈✈✈✈✈ package algorithm.归并;import java.util.Arrays;public class Fenzhi {public static void main(String[] args) {int [] arr {8,4,1,2,7,6,3,5};mergeSort(arr, 0, arr.length-1, new int[8]);System.out.println(Arrays.toString(a…

【Godot4自学手册】第三十七节钥匙控制开门

有些日子没有更新了&#xff0c;实在是琐事缠身啊&#xff0c;今天继续开始自学Godot4&#xff0c;继续完善地宫相关功能&#xff0c;在地宫中安装第二道门&#xff0c;只有主人公拿到钥匙才能开启这扇门&#xff0c;所以我们在合适位置放置一个宝箱&#xff0c;主人公开启宝箱…

Tcpdump -r 解析pcap文件

当我们使用命令抓包后&#xff0c;想在命令行直接读取筛选怎么办&#xff1f;-r参数就支持了这个 当你使用 tcpdump 的 -r 选项读取一个之前捕获的数据包文件&#xff0c;并想要筛选指定 IP 地址和端口的包时&#xff0c;你可以在命令中直接加入过滤表达式。这些过滤表达式可以…

2024 年 Web 前端开发趋势

希腊哲学家赫拉克利特认为&#xff0c;变化是生命中唯一不变的东西。这句话适用于我们的个人生活、行业和职业领域。 尤其是前端开发领域&#xff0c;新技术、开发趋势、库和框架不断涌现&#xff0c;变化并不陌生。最近发生的一些事件正在改变开发人员构建网站和 Web 应用的方…

git 分支-变基

在git中&#xff0c;将一个分支的更改集成到另一个分支有两种主要方式&#xff1a;合并&#xff08;merge&#xff09;和变基&#xff08;rebase&#xff09;。在本节中&#xff0c;将学习什么是变基&#xff0c;如何执行变基操作&#xff0c;为什么它是一个非常强大的工具&…

【题目】【信息安全管理与评估】2022年国赛高职组“信息安全管理与评估”赛项样题9

【题目】【信息安全管理与评估】2022年国赛高职组“信息安全管理与评估”赛项样题9 信息安全管理与评估 网络系统管理 网络搭建与应用 云计算 软件测试 移动应用开发 任务书&#xff0c;赛题&#xff0c;解析等资料&#xff0c;知识点培训服务 添加博主wx&#xff1a;liuliu548…

网工交换基础——MUX VLAN

前言&#xff1a; MUX VLAN&#xff08;Multiplex VLAN&#xff0c;多复用VLAN&#xff09;提供了一种通过VLAN进行网络资源控制的机制。例如&#xff0c;在企业网络中&#xff0c;企业员工和企业客户可以访问企业的服务器。对于企业来说&#xff0c;希望企业内部员工之…

Docker安装(一)

一、安装Docker 服务器系统&#xff1a;centos 7 1.本地有docker的首先卸载本机docker yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-selinux \docker-engine-selinux \dock…