代码随想录算法训练营第13天|● 239. 滑动窗口最大值 ● 347.前 K 个高频元素


前言

今天是队列专项:都需要思考,不是很难,重新做

链接

原来csdn的粉丝都是机器人,我说怎么那么多人,好吧,我随便记录一下,既然没有人看

239. 滑动窗口最大值

在这里插入图片描述
💖非常典型的队列题目

思路

暴力求解: 遍历一遍的过程中每次从窗口中再找到最大的数值,这样很明显是O(n × k)的算法
总体思路: 此时我们需要一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么
在这里插入图片描述
保证队列里的元素单调递减: 队列里面放入一个元素就和前面一个元素比较,如果比它大就pop前面的,如果比它小再放进去; [有动图]

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

  • pop(value):如果窗口向后移动要移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  • push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出【大于入口说明大于所有的,前面所有的都弹出】,直到push元素的数值小于等于队列入口元素的数值为止
  • 在这里插入图片描述

自己写 注意事项【最好重新做一遍】

  1. python的deque的index也是从0开始从最左边开始标;
  2. 怎么方便怎么来:pop和popleft都用到了
  3. 所有的pop操作都需要判断是否为空【我在写myqueue的pop就忘了】
  4. push的def再看一下写法
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(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: 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

347.前 K 个高频元素

在这里插入图片描述

思路

知识补充:大顶堆和小顶堆注意这个里面使用大小顶堆进行排序

  • 本题中使用小顶堆:因为小顶堆最上面的是最小的,会弹出去,把小的都弹出去之后留下大的;
  • python使用heapq来做顶堆:heapqpush(ans, ele_v)的时候放入堆,它会自动排序;headpop弹出最顶部的那个;

这道题目主要涉及到如下三块内容:

  1. 要统计元素出现频率
  2. 对频率排序
  3. 找出前K个高频元素

💘统计频率:使用map来做呀,这时候要记住了
排序:通常是使用快排,返回前k个,但是由于这里只需要k,所以只用对k个元素进行维护;
具体做法:遍历的时候使用小顶堆,当小顶堆长度大于k的时候,就pop出去根节点最小的,这样保证留下来的都是k大的;
但是由于小顶堆pop出去的都是从小到大,题目要求从大到小;方法:在装入result数组 的时候,从后往前装–1
在这里插入图片描述

方法一 使用顶堆

注意事项

  1. 在 搭建小顶堆的时候,push进去的是键值对,但是value要写在前面,因为根据value来进行堆的构建:heapq.heappush(pri_que, (freq, key))
    相应的,pop之后也是一对以(freq, key)排序的,加入result的时候append ele[1]:heapq.heappop(pri_que)[1]
  2. 要记住了:map_[nums[i]] = map_.get(nums[i], 0) + 1这一行代码来统计频率
  3. 创建小顶堆也是用的数组;pri_que
  4. 倒序修改list需要先创建一个那么大的list[0]*K
    在这里插入图片描述
    在这里插入图片描述
import heapq
class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        map_ = {}
        for i in range(len(nums)):
            map_[nums[i]] = map_.get(nums[i],0)+1
        pri_que = []
        for key, value in map_.items():
            heapq.heappush(pri_que,(value,key))
            if len(pri_que)>k:
                heapq.heappop(pri_que)
        result = [0]*k
        for i in range(k-1,-1,-1):
            result[i] = heapq.heappop(pri_que)[1]
        return result

方法二 使用字典

普通排序的方法

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # 使用字典统计数字出现次数
        time_dict = defaultdict(int)
        for num in nums:
            time_dict[num] += 1
        # 更改字典,key为出现次数,value为相应的数字的集合
        index_dict = defaultdict(list)
        for key in time_dict:
            index_dict[time_dict[key]].append(key)
        # 排序
        key = list(index_dict.keys())
        key.sort()
        result = []
        cnt = 0
        # 获取前k项
        while key and cnt != k:
            result += index_dict[key[-1]]
            cnt += len(index_dict[key[-1]])
            key.pop()

        return result[0: k]

总结-栈与队列的总结

在这里插入图片描述
知识点的补充
在这里插入图片描述

  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值