Description
給定一個整數陣列
nums和一個整數k(視窗大小),視窗從左向右滑動,每次移動一格。回傳每個視窗位置的最大值。
Example:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3 Output: [3,3,5,5,6,7]
Intuition#
用單調遞減的雙端佇列(Deque)維護視窗內的「候選最大值」,佇列頭部永遠是當前最大值。
- 暴力法每個視窗都要 O(k) 找最大值,太慢
- 維護一個單調遞減的 Deque,存放元素的索引
- 新元素進入時,從尾部移除所有比它小的元素(因為它們不可能再成為最大值)
- 頭部元素超出視窗範圍時從頭部移除
Approaches#
1: Brute Force#
- 概念: 對每個視窗位置,遍歷視窗內所有元素找最大值
- 時間複雜度:
O(n * k) - 空間複雜度:
O(1)(不含輸出)
Brute Force 程式碼
class Solution {
fun maxSlidingWindow(nums: IntArray, k: Int): IntArray {
val result = IntArray(nums.size - k + 1)
for (i in result.indices) {
var max = nums[i]
for (j in i until i + k) {
max = maxOf(max, nums[j])
}
result[i] = max
}
return result
}
}⭐2: Monotonic Deque#
- 概念: 維護一個單調遞減的雙端佇列,佇列頭部即為當前視窗最大值
- 時間複雜度:
O(n),每個元素最多入隊出隊各一次 - 空間複雜度:
O(k)
class Solution {
fun maxSlidingWindow(nums: IntArray, k: Int): IntArray {
val result = IntArray(nums.size - k + 1)
val deque = ArrayDeque<Int>() // 存索引
for (right in nums.indices) {
// 移除尾部所有比當前元素小的
while (deque.isNotEmpty() && nums[deque.last()] <= nums[right]) {
deque.removeLast()
}
deque.addLast(right)
// 移除超出視窗的頭部元素
if (deque.first() < right - k + 1) {
deque.removeFirst()
}
// 視窗形成後記錄結果
if (right >= k - 1) {
result[right - k + 1] = nums[deque.first()]
}
}
return result
}
}Deque 中存的是索引而非值,這樣才能判斷頭部元素是否超出視窗範圍。比較大小時用
nums[deque.last()]取值。
🔑 Takeaways#
- Pattern: 單調 Deque(Monotonic Deque)
- 關鍵技巧: 單調 Deque 是解決「滑動視窗中的最值」問題的標準工具。核心思想:如果一個元素比後來的元素小且位置更靠前,它永遠不可能成為答案,可以安全移除