滑动窗口最大值

给你一个整数数组 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

< 此题难度非常高 我自己的实现没有通过测试 卡在了时间复杂度上 后来根据题解修改才通过
< 有两种思路 单调队列和预处理 预处理更是非常巧妙

  • 单调队列
package main

func main() {

	// temp := trap([]int{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1})
	temp := maxSlidingWindow([]int{1, 3, -1, -3, 5, 3, 6, 7}, 3)
	// println(temp)
	for _, v := range temp {
		println(v)
	}
	// for i := 0; i < len(temp); i++ {
	// 	for j := 0; j < len(temp[i]); j++ {
	// 		print(temp[i][j], " ")
	// 	}
	// 	println()
	// }
}

// maxSlidingWindow 计算滑动窗口中的最大值
// nums: 给定的整数数组
// k: 滑动窗口的大小
// 返回值: 一个整数数组,包含每个滑动窗口中的最大值
func maxSlidingWindow(nums []int, k int) []int {
	// q: 使用切片来模拟一个单调递减队列(实际上是一个索引队列)
	// 它保存的是数组nums中元素的索引,且索引对应的元素值在队列中是单调递减的
	q := []int{}

	// push: 一个内部函数,用于向队列中添加元素
	push := func(i int) {
		// 当队列不为空,且当前元素的值大于或等于队列尾部元素的值时
		// 移除队列尾部的元素,因为当前元素的值更大,可能成为新的窗口最大值
		for len(q) > 0 && nums[i] >= nums[q[len(q)-1]] {
			q = q[:len(q)-1]
		}
		// 将当前元素的索引添加到队列尾部
		q = append(q, i)
	}

	// 初始化窗口,将前k个元素的索引添加到队列中
	for i := 0; i < k; i++ {
		push(i)
	}

	// n: 数组nums的长度
	n := len(nums)

	// ans: 用于保存每个滑动窗口中的最大值的切片
	// 初始容量为1,但预计最终容量为n-k+1(因为总共有n-k+1个滑动窗口)
	ans := make([]int, 1, n-k+1)

	// 队列头部的索引对应的元素就是当前窗口的最大值
	// 将第一个窗口的最大值添加到结果切片中
	ans[0] = nums[q[0]]

	// 从第k个元素开始遍历到数组末尾
	// 因为前k个元素已经初始化过窗口和结果切片
	for i := k; i < n; i++ {
		// 将新元素的索引添加到队列中
		push(i)

		// 如果队列头部的索引已经超出当前窗口的范围(即小于i-k+1),则移除它
		// 因为这个索引对应的元素不再是当前窗口的一部分
		for q[0] <= i-k {
			q = q[1:]
		}

		// 队列头部的索引对应的元素就是当前窗口的最大值
		// 将它添加到结果切片中
		ans = append(ans, nums[q[0]])
	}

	// 返回结果切片
	return ans
}

  • 预处理
package main

func main() {

	// temp := trap([]int{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1})
	temp := maxSlidingWindow([]int{1, 3, -1, -3, 5, 3, 6, 7}, 3)
	// println(temp)
	for _, v := range temp {
		println(v)
	}
	// for i := 0; i < len(temp); i++ {
	// 	for j := 0; j < len(temp[i]); j++ {
	// 		print(temp[i][j], " ")
	// 	}
	// 	println()
	// }
}

// maxSlidingWindow 函数接收一个整数数组 nums 和一个整数 k,返回滑动窗口大小为 k 的所有子数组中的最大值组成的数组
func maxSlidingWindow(nums []int, k int) []int {
	n := len(nums)
	prefixMax := make([]int, n) // 用于存储每个窗口的前缀最大值
	suffixMax := make([]int, n) // 用于存储每个窗口的后缀最大值

	// 计算前缀最大值
	for i, v := range nums {
		if i%k == 0 {
			prefixMax[i] = v
		} else {
			prefixMax[i] = max(prefixMax[i-1], v)
		}
	}

	// 计算后缀最大值
	for i := n - 1; i >= 0; i-- {
		if i == n-1 || (i+1)%k == 0 {
			suffixMax[i] = nums[i]
		} else {
			suffixMax[i] = max(suffixMax[i+1], nums[i])
		}
	}

	ans := make([]int, n-k+1) // 存储最终结果
	for i := range ans {
		ans[i] = max(suffixMax[i], prefixMax[i+k-1])
	}
	return ans
}

// max 函数用于返回两个整数中的较大值
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
posted @ 2024-06-03 10:08  烟熏咸鱼干  阅读(6)  评论(0编辑  收藏  举报