栈与队列 滑动窗口
239. 滑动窗口最大值
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入: 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
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
解题思路
采用单调队列来实现
单调队列(Monotonic Queue)是一种数据结构,它可以在的时间内完成以下两个操作:
在队尾添加一个元素。 删除队头元素。 它的特殊之处在于,它可以维护队列中元素的单调性。具体来说,如果队列中的元素单调递增(或单调递减),那么这个队列就是单调队列。
单调队列常用于滑动窗口问题,也可以用来解决一些其他的问题。例如,可以使用单调队列来求解一个数组中每个元素的前/后个最大/最小值等。
实现代码
class MonotonicQueue {
var queue = [Int]()
func push(_ num:Int) {
while !queue.isEmpty && num > queue.last! {
queue.removeLast()
}
queue.append(num)
}
func pop(_ num:Int) {
// 因为比queue.first 小的数 在 push 进单调栈的时候就已经被移除了
// 所以这里只需要处理 num == queue.first 的情况
if num == queue.first {
queue = Array(queue[1..<queue.count])
}
}
func max() -> Int {
return queue.first!
}
}
func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] {
var res = [Int]()
let queue = MonotonicQueue()
for i in 0..<nums.count {
if i < k-1 {
queue.push(nums[i])
} else {
// 入栈
queue.push(nums[i])
// 找最大值
res.append(queue.max())
// 出栈
queue.pop(nums[i+1-k])
}
}
return res
}
347. 前 K 个高频元素
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
1 <= nums.length <= 105
k
的取值范围是[1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前
k
个高频元素的集合是唯一的
进阶: 你所设计算法的时间复杂度 必须 优于 O(n log n)
,其中 n
**是数组大小。
实现逻辑
方案一: 将元素 与 元素出现的次数存入字典中,将这个字典按照 value 从大到小进行遍历 将遍历之后的字典value进行取值
方案二: 将元素 与 元素出现的次数存入字典中 将字典的value 存入一个指定大小的 小顶堆中 ,这样小顶堆中保存的就是高频元素出现的次数 遍历 map 将value 属于这个小顶堆的key 找出来
实现源码
class PriorityQueue {
var list:[Int]
var maxSize:Int
init(maxSize:Int) {
self.list = [Int]()
self.maxSize = maxSize
}
private func leftChildIndex(index:Int) -> Int {
return 2 * index + 1
}
private func rightChildIndex(index:Int) -> Int {
return 2 * (index + 1)
}
private func parentIndex(index: Int) -> Int {
return (index - 1) / 2
}
private func siftUp(_ index: Int) {
var childIndex = index
var parentIndex = self.parentIndex(index: index)
while childIndex > 0 && list[parentIndex] > list[childIndex] {
self.list.swapAt(parentIndex, childIndex)
childIndex = parentIndex
parentIndex = self.parentIndex(index: childIndex)
}
}
private func siftDown(_ index: Int) {
let parentIndex = index
let leftChildIndex = self.leftChildIndex(index: parentIndex)
let rightChildIndex = self.rightChildIndex(index: parentIndex)
// 如果左边的子节点 大于 链表长度 说明 已经不需要下沉
if leftChildIndex > list.count {
return
}
// minChildIndex 表示左右子节点中 小的那个 的index
var minChildIndex = leftChildIndex
// 如果右边节点存在 找到 左右子节点中 小的那个 的index
if rightChildIndex < list.count {
minChildIndex = list[leftChildIndex] < list[rightChildIndex] ? leftChildIndex : rightChildIndex
}
// 如果下的那个的index 小于 这个节点 ,则交换他们 ,然后 继续下沉
if list[minChildIndex] < list[parentIndex] {
list.swapAt(parentIndex, minChildIndex)
siftDown(minChildIndex)
}
}
func push(num:Int) {
if list.count < maxSize {
list.append(num)
siftUp(list.count-1)
} else {
// 顶部是最小的 如果比顶部大 则加入进来 否则不需要加入
if list.first! < num {
list[0] = num
siftDown(0)
}
}
}
func pop() -> Int? {
guard !list.isEmpty else { return nil }
// 将数组的前后进行交换
list.swapAt(0, list.count - 1)
// 删除尾部元素
let removed = list.removeLast()
// 同时顶部元素下沉
siftDown(0)
return removed
}
}
class Solution_StackAndQueue_Day13_2 {
func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] {
var map = [Int: Int]()
for i in 0..<nums.count {
map[nums[i],default: 0] += 1
}
let queue = PriorityQueue(maxSize: k)
for num in map.values {
queue.push(num: num)
}
let set = Set(queue.list)
var res = [Int]()
for key in map.keys {
if set.contains(map[key]!) {
res.append(key)
}
}
return res
}
}