一、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,一道困难题,一道中等题,还是花了很长时间理解,特别是大小顶堆的知识点还需要补充,加油!