算法学习|Day13-栈与队列|Leetcode239滑动窗口最大值,Leetcode347前 K 个高频元素

一、Leetcode239(滑动窗口最大值)

题目描述

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

返回 滑动窗口中的最大值

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7
输入:nums = [1], k = 1
输出:[1]

题目链接:力扣题目链接

解题思路

  • 滑动窗口很快想到双指针的方法(但是运行会导致时间超限)
  • 使用单调队列

方法:单调队列

  • 简单来说,滑动窗口右移一位:相当于pop左边1个,push右边1个
  • 实现单调:每次push一个新的元素都将其所有小于它的元素弹出,保证队列中最左边的数最大(减少查找最大数的时间复杂度)
  • 实现滑动窗口:如果需要pop的元素==队列最左元素,则先弹出,不相等则不操作(说明push的过程中已经弹出),然后push进去一个
  • 下面有滑动过程示意图!

暴力解法:(超出时间限制)

# 暴力解法超出时间限制
class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        stack = []   #存每次滑动窗口中的最大值
        left = 0     #左指针
        right = k-1  #右指针

        while right <= len(nums)-1: #防止右指针出界
            max = nums[left]
            for i in nums[left: right+1]:
                if i > max:
                    max = i
            stack.append(max)
            left += 1
            right += 1
        return stack
        

单调队列解法:
滑动窗口6次示意图:
在这里插入图片描述

class Myqueue:
    # 构造函数,初始化队列
    def __init__(self):
        self.que = deque()

    # 当需要弹出的元素==队列头元素相同时,pop
    def pop(self, value):
        if self.que and value == self.que[0]:
            self.que.popleft()

    # 当加入的元素大于队列尾部元素时,依次全部pop,保证单调队列
    def push(self, value):
        while self.que and value > self.que[-1]:
            self.que.pop()
        self.que.append(value)

    # 收集每个滑动窗口内的最大值
    def maxnum(self):
        return self.que[0]


class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        stack = []
        que = Myqueue()
        # 先放入K个元素
        for i in range(k):
            que.push(nums[i])
        stack.append(que.maxnum())
        # 滑动窗口依次向右移动一位
        for i in range(k, len(nums)):
            que.pop(nums[i - k])
            que.push(nums[i])
            stack.append(que.maxnum())
        return stack

总结

  • 减少时间复杂度->减少查找最大值的过程(单调队列!)


二、Leetcode347(前 K 个高频元素)

题目描述

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

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

题目链接:力扣题目链接

解题思路

  • 怎么统计元素频率?
  • 如何排序保留前k个?
  • 如何输出?

方法:小顶堆

  • 用字典统计元素出现的频率,key=nums[i],value默认0,出现一次++
  • 使用小顶堆排序,只保留k个
  • 堆顶是小的,所以需要倒序输出
import heapq

class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        # map求元素频率
        map_ = {}
        for i in range(len(nums)):
            map_[nums[i]] = map_.get(nums[i], 0) + 1

        # 用小顶堆排序
        pri_que = []
        for key, freque in map_.items():
            heapq.heappush(pri_que, (freque, key))
            if len(pri_que) > k:
                heapq.heappop(pri_que)

        # 输出前k个高频(小顶堆先弹出最小的,所以要倒序输出)
        result = [0] * k
        for i in range(k-1, -1, -1):
            result[i] = heapq.heappop(pri_que)[1]
        return result


总结

  • map_[nums[i]] = map_.get(nums[i], 0) + 1,用字典累计

  • heapq.heappush(pri_que, (freque, key)),
    heapq.heappop(pri_que),小顶堆push/pop操作

  • 从序列中选择最大的K个数(小顶堆)
    从序列中选择最小的K个数(大顶堆)
    大小顶堆知识点周末补上~



心得:坚持到Day13,一道困难题,一道中等题,还是花了很长时间理解,特别是大小顶堆的知识点还需要补充,加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值