栈和队列(队列的应用)[三]

这篇具有很好参考价值的文章主要介绍了栈和队列(队列的应用)[三]。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、滑动窗口最大值

思想:这道题属于困难题,不容易想到解决办法。对于“最大值”,我们可以想到一种非常合适的数据结构,那就是优先队列(堆),其中的大根堆可以帮助我们实时维护一系列元素中的最大值。我们将数组nums的前k个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,但堆顶的元素就是堆中所有元素的最大值,这个最大值可能并不在滑动窗口出口,返回不了我们想要的“最大值”。
顺着思路继续优化,考虑使用单调队列,可以实现题目要求!保持单调队列里单调递减,此时队列出口元素就是窗口里最大元素。

滑动窗口最大值(leetcode 239. )

这个队列的C++代码

class MyQueue{
public:
	void pop(int value){
	}
	void push(int value){
	}
	int front(){
		return que.front();
	}
};

放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,调用que.pop(滑动窗口中移除元素的数值),que.push(滑动窗口添加元素的数值),然后que.front()就返回我们要的最大值。
C++中没有直接支持单调队列,需要我们自己来实现一个单调队列
伪代码

#C++中deque是stack和queue默认的底层实现容器
deque<int> que;#queue在没有指定容器的情况下,deque就是默认底层容器。
#每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
#同时pop前判断队列当前是否为空
pop(int val){
	if(!que.empty() && val==que.front()){
		que.pop_front(); }
}
#如果push数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
#这样就保持了队列里的数值是单调从大到小的了。
push(int val){
	if (!que.empty())&& val>que.back()){#push进来的元素大于队列
		que.pop_back();}
	}
	que.push_back(value);
}
#查询当前队列里的最大值 直接返回队列前端也就是front就可以了
int front(){
	return que.front();
}

这样就用deque实现了一个单调队列!具体滑动窗口实现看下面的代码:

from collections import deque

class MyQueue: #单调队列(从大到小)
    def __init__(self):
        self.queue = deque() #这里需要使用deque实现单调队列,直接使用list会超时
    #每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
    #同时pop之前判断队列当前是否为空。
    def pop(self, value):
        if self.queue and value == self.queue[0]:
            self.queue.popleft()#list.pop()时间复杂度为O(n),这里需要使用collections.deque()  
    #如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
    #这样就保持了队列里的数值是单调从大到小的了。
    def push(self, value):
        while self.queue and value > self.queue[-1]:
            self.queue.pop()
        self.queue.append(value)
        
    #查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
    def front(self):
        return self.queue[0]
    
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        que = MyQueue()
        result = []
        for i in range(k): #先将前k的元素放进队列
            que.push(nums[i])
        result.append(que.front()) #result 记录前k的元素的最大值
        for i in range(k, len(nums)):
            que.pop(nums[i - k]) #滑动窗口移除最前面元素
            que.push(nums[i]) #滑动窗口前加入最后面的元素
            result.append(que.front()) #记录对应的最大值
        return result
时间复杂度: O(n)
空间复杂度: O(k)

题目中使用单调队列的时间复杂度是 O(n)。空间复杂度因为我们定义一个辅助队列,所以是O(k)。注意: 单调队列不是一成不变的,而是不同场景不同写法,总之要保证队列里单调递减或递增的原则,所以叫做单调队列。 另C++中deque是栈stack和队列queue默认的底层实现容器,deque是可以两边扩展的,而且deque里元素并不是严格的连续分布的。

二、求前 K 个高频元素

前 K 个高频元素(leetcode 347.)

思路:
本题主要涉及问题有三个:
1.统计元素出现频率(可以用map来实现)
2.对频率排序(使用容器适配器就是优先级队列)
3.找出前k个高频元素
注意: 这里解释一下优先级队列,它其实是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。而且优先级队列内部元素是自动依照元素的权值排列。默认情况下(也称缺省情况下),优先级队列priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)。
为了帮助大家理解,再解释一下堆,堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。如果父亲结点是大于等于左右孩子就是大顶堆(堆头是最大元素),小于等于左右孩子就是小顶堆(堆头是最小元素)。最小堆适用于找到最小值或前 k 个最小值的场景,而最大堆适用于找到最大值或前 k 个最大值的场景。
实现堆的常用方法是使用数组,并根据插入和删除操作来维护堆的性质。
使用数组实现最小堆的基本步骤:
1.创建一个空数组,用于存储堆的元素。
2.实现插入操作(Insertion):
将新元素添加到数组的末尾。
通过比较该元素与其父节点的值,并交换它们的位置,以确保堆的性质。重复此步骤直到满足堆的性质为止。
3实现删除操作(Deletion):
移除堆的根节点(最小值)。
将数组的最后一个元素放置在根节点的位置。
通过与其子节点比较,并交换位置,将该元素下沉到合适的位置,以满足堆的性质。重复此步骤直到满足堆的性质为止。
大顶堆(堆头是最大元素),小顶堆(堆头是最小元素),如果懒得自己实现的话,就直接用priority_queue(优先级队列)就可以了,底层实现都是一样的,从小到大排就是小顶堆,从大到小排就是大顶堆。

import heapq
class Solution:
	def topKFrequent(self,nums,k):
		#用字典统计nums中每个元素出现次数
		map_={}#nums[i]对应的元素次数
		for i in range(len(nums)):
	#使用字典的get()方法获取字典map_中键为nums[i]的的出现次数,如果键不存在,则返回默认值0。
			map_[nums[i]]=map_.get(nums[i],0)+1#将获取到的出现次数加1,并将更新后的值存储回字典中。
		#对频率排序
		#定义一个小顶堆,大小为k
		pri_que=[]
		#用固定大小为k的小顶堆,扫描所有频率的数值
		for key,freq in map_.items():
			heapq.heappush(pri_que,(freq,key))#将(freq,key)插入堆pri_que中并保持堆的性质。这意味着插入后,堆中的最小元素仍然位于堆顶。
			if len(pri_que)>k:#如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
				heapq.heappop(pri_que)#弹出并返回堆pri_que中的最小元素。该操作会同时调整堆,以保持堆的性质。
		#找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
        result = [0] * k#创建一个长度为k的列表result,并将其所有元素初始化为0
        for i in range(k-1, -1, -1):#使用一个循环从k-1到0,以递减的顺序迭代。
            result[i] = heapq.heappop(pri_que)[1]#在循环中,使用 heapq.heappop(pri_que)从优先队列pri_que中弹出并返回最小(或最大)的元素。
        return result
#时间复杂度:O(nlogk)
#空间复杂度:O(n)	

这时候有同学会问为什么不用快排呢,因为使用快排的话,要将字典map转换为向量vector的结构,然后对整个数组进行排序,而这种场景下,我们其实只需要维护k个有序的序列就可以了,所以使用优先级队列是最优的解法。
那么为什么题目要求是求前 K 个高频元素,我么没有使用大顶堆而是使用小顶堆? 因为,使用大顶堆就要把所有元素都进行排序,而且每次弹出都把最大的元素弹出去了,那么怎么保留下来前K个高频元素呢。所以我们要用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。
(ps:近期都是关于数据结构基础知识分享,感兴趣的同学可以关注本人公众号:HEllO算法笔记)文章来源地址https://www.toymoban.com/news/detail-487663.html

到了这里,关于栈和队列(队列的应用)[三]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 力扣日记10.30-【栈与队列篇】滑动窗口最大值

    日期:2023.10.30 参考:代码随想录、力扣 题目描述 难度:困难 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1: 示例 2:

    2024年02月06日
    浏览(47)
  • 滑动窗口最大值 | 单调队列解题思路与实现,前 K 个高频元素

    学习LeetCode 239与347题目的解题思路与实现方法,包括单调队列与优先级队列(堆)的应用。掌握滑动窗口的最大值与前K个高频元素的解决方案。

    2024年02月13日
    浏览(50)
  • 239. 滑动窗口最大值

    力扣题目链接   (opens new window) 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回滑动窗口中的最大值。 进阶: 你能在线性时间复杂度内解决此题吗? 提示:

    2024年02月15日
    浏览(46)
  • 华为OD-滑动窗口最大值

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

    2024年02月11日
    浏览(43)
  • leetcode-239-滑动窗口最大值

    题意描述: 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例: 输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动

    2024年02月07日
    浏览(50)
  • LeetCode239.滑动窗口最大值

    看到这道题我就有印象, 我在剑指offer里面做过这道题,我记得当时用的是优先队列,然后我脑子里一下子就有了想法,拿优先队列作为窗口,每往右移动一步,把左边的数remove掉,把右边的数add进来,然后把队头,也就是窗口中最大的元素放入答案数组,然后就写出了如下

    2024年02月11日
    浏览(45)
  • 【LeetCode热题100】【子串】滑动窗口最大值

    题目 给你一个整数数组  nums ,有一个大小为  k   的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的  k  个数字。滑动窗口每次只向右移动一位。 返回  滑动窗口中的最大值  。 示例 1: 示例 2: 提示: 1 = nums.length = 105 -104 = nums[i] = 104 1 =

    2024年01月19日
    浏览(49)
  • 力扣热门100题之滑动窗口最大值【困难】

    给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1: 输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置

    2024年02月16日
    浏览(43)
  • LeetCode 239.滑动窗口的最大值 Hot100 单调栈

    给你一个整数数组  nums ,有一个大小为  k   的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的  k  个数字。滑动窗口每次只向右移动一位。 返回  滑动窗口中的最大值  。 示例 1: 示例 2: 提示: 1 = nums.length = 105 -104 = nums[i] = 104 1 = k = num

    2024年02月20日
    浏览(51)
  • day12 | 239. 滑动窗口最大值、347.前 K 个高频元素、

    目录: 题目链接: https://leetcode.cn/problems/sliding-window-maximum/ https://leetcode.cn/problems/top-k-frequent-elements/ 239. 滑动窗口最大值 给你一个整数数组  nums ,有一个大小为  k  **的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的  k  个数字。滑动窗口每

    2024年02月08日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包