66 lines
1.9 KiB
Python
66 lines
1.9 KiB
Python
"""Sliding window maximum (LeetCode 239).
|
|
|
|
Returns the maximum of each contiguous window of size `k` over `nums`
|
|
in O(n) time using a monotonic deque of indices.
|
|
|
|
Subtle points handled:
|
|
- empty input / k <= 0 -> []
|
|
- k == 1 -> nums copy
|
|
- k >= len(nums) -> [max(nums)] (or [] if empty)
|
|
- duplicate values (deque keeps indices, not values, to handle expiry)
|
|
- negative numbers
|
|
"""
|
|
from collections import deque
|
|
from typing import List
|
|
|
|
|
|
def sliding_window_max(nums: List[int], k: int) -> List[int]:
|
|
if not nums or k <= 0:
|
|
return []
|
|
if k == 1:
|
|
return list(nums)
|
|
n = len(nums)
|
|
if k >= n:
|
|
return [max(nums)]
|
|
|
|
dq = deque() # indices, nums[dq[0]] is current max; values strictly decreasing
|
|
out: List[int] = []
|
|
for i, x in enumerate(nums):
|
|
# evict indices outside window
|
|
while dq and dq[0] <= i - k:
|
|
dq.popleft()
|
|
# maintain decreasing values
|
|
while dq and nums[dq[-1]] <= x:
|
|
dq.pop()
|
|
dq.append(i)
|
|
if i >= k - 1:
|
|
out.append(nums[dq[0]])
|
|
return out
|
|
|
|
|
|
def _brute(nums, k):
|
|
if not nums or k <= 0:
|
|
return []
|
|
return [max(nums[i:i + k]) for i in range(len(nums) - k + 1)]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import random
|
|
rng = random.Random(123)
|
|
for _ in range(5000):
|
|
n = rng.randint(0, 40)
|
|
nums = [rng.randint(-20, 20) for _ in range(n)]
|
|
k = rng.randint(0, n + 3) if n else rng.randint(0, 5)
|
|
got = sliding_window_max(nums, k)
|
|
# compute expected via brute respecting edge rules
|
|
if not nums or k <= 0:
|
|
exp = []
|
|
elif k == 1:
|
|
exp = list(nums)
|
|
elif k >= len(nums):
|
|
exp = [max(nums)]
|
|
else:
|
|
exp = [max(nums[i:i + k]) for i in range(len(nums) - k + 1)]
|
|
assert got == exp, (nums, k, got, exp)
|
|
print("self-check: 5000 random cases passed")
|