题目
给你一个整数数组 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
解析
这道题是使用单调队列(注意既不是优先级队列top K,也不是普通的先入先出队列,而是单调队列),队列中的队首是最大值;
在压入队列时,判断队列末尾的元素值如果小于要加入的值,则循环删除,保证队首最大;
func maxSlidingWindow(nums []int, k int) []int {
var ans []int
var queue []int
for i, num := range nums {
// 1.入队列(元素入队尾,同时要满足队列的单调性)
for len(queue) > 0 && nums[queue[len(queue)-1]] <= num {
queue = queue[:len(queue)-1]
}
queue = append(queue, i) // 入的是下标
// 2.出队列(元素离开队首),出队列的条件是此时的下标比减去窗口起始位置的下标,大于K
if i-queue[0] >= k {
queue = queue[1:]
}
// 3.记录答案
if i >= k-1 { // 这一步主要是用于拦截最开始长度不够K的时候,比如K=3,i是下标要大于等于2的时候,才够第一个窗口
// 由于队首到队尾是单调递减,所以窗口最大值就是队首
ans = append(ans, nums[queue[0]])
}
}
return ans
}
也可以用下面的代码,都差不多,上面在数组中存的是下标,下面存的是具体的值
type MyQueue struct {
queue []int
}
func NewMyQueue() *MyQueue {
return &MyQueue{
queue: make([]int, 0),
}
}
func (m *MyQueue) Front() int {
return m.queue[0]
}
func (m *MyQueue) Back() int {
return m.queue[len(m.queue)-1]
}
func (m *MyQueue) Empty() bool {
return (len(m.queue) == 0)
}
//上面这三个相当于C++中可以直接使用的dqueue
func (m *MyQueue) Pop(val int) {
if !m.Empty() && val == m.Front() { //只有队列的队首元素等于需要弹的元素时才弹出
m.queue = m.queue[1:]
}
}
func (m *MyQueue) Push(val int) {
for !m.Empty() && val > m.Back() { //如果要加进来的元素比队列中末尾元素大,循环删除
m.queue = m.queue[:len(m.queue)-1]
}
m.queue = append(m.queue, val)
}
func maxSlidingWindow(nums []int, k int) []int {
queue := NewMyQueue()
res := []int{}
//先将前K个元素加入队列
for i:=0; i < k; i++ {
queue.Push(nums[i]) //比如是1 3 -1,则此时队列中是3 -1
}
//记录前K个元素的最大值
res = append(res, queue.Front())
for i:=k; i < len(nums); i++ {
queue.Pop(nums[i-k]) //先弹出最开头的元素、
queue.Push(nums[i]) //压入新的元素
res = append(res, queue.Front()) //将单调栈中的第一个元素(最大的)放进结果集中
}
return res
}