Monotonic Deque: Unlocking O(N) Sliding Window Maximums
Struggling with slow O(NK) sliding window maximums? Master the monotonic deque for linear time efficiency!
π The Monotonic Deque (Double-Ended Queue) is a powerful data structure that efficiently finds the maximum (or minimum) within a fixed-size sliding window in O(N) time, far superior to the naive O(NK) approach.
π― System Scenario & Requirements
System Goal
π The Monotonic Deque (Double-Ended Queue) is a powerful data structure that efficiently finds the maximum (or minimum) within a fixed-size sliding window in O(N) time, far superior to the naive O(NK) approach.
Actors & Components
- Application or API tier
- Core service under design
- Operator or SRE observer
Functional Requirements
- π The Monotonic Deque (Double-Ended Queue) is a powerful data structure that efficiently finds the maximum (or minimum) within a fixed-size sliding window in O(N) time, far superior to the naive O(NK) approach.
- It's crucial for optimizing stream processing, financial analysis, and algorithmic challenges where window operations are frequent.
- Imagine you're processing a continuous stream of data, like stock prices or sensor readings, and you need to find the maximum value within a fixed-size 'window' that slides across the data.
Constraints & Assumptions
- Operational latency, consistency levels, and network partition boundaries must be enforced.
β‘ Execution Stages & Decision Trace
Stage 1 β Code Example: Sliding Window Maximum
Apply core-algorithm-patterns to Code Example: Sliding Window Maximum.
The components in βCode Example: Sliding Window Maximumβ follow the mechanism explained in this stage.
Deep Dive
Deep Dive: Stage 1 β Code Example: Sliding Window Maximum
from collections import deque
def max_sliding_window(nums, k):
if not nums or k == 0:
return []
result = []
# Deque stores indices
# Elements are stored in decreasing order of their values
dq = deque()
for i in range(len(nums)):
# 1. Remove elements from the front that are out of the current window
if dq and dq[0] == i - k:
dq.popleft()
# 2. Remove elements from the back that are smaller than the current element
# These can no longer be the maximum because nums[i] is greater and appears later
while dq and nums[dq[-1]] <= nums[i]:
dq.pop()
# 3. Add current element's index to the back of the deque
dq.append(i)
# 4. If the window is fully formed, the front of the deque is the maximum
if i >= k - 1:
result.append(nums[dq[0]])
return result
# Example Usage:
nums = [1, 3, -1, -3, 5, 3, 6, 7]
k = 3
# Expected output: [3, 3, 5, 5, 6, 7]
print(f"Array: {nums}, Window Size: {k}")
print(f"Sliding Window Maximums: {max_sliding_window(nums, k)}")
nums_small = [1, 2, 3, 4, 5]
k_small = 2
# Expected output: [2, 3, 4, 5]
print(f"Array: {nums_small}, Window Size: {k_small}")
print(f"Sliding Window Maximums: {max_sliding_window(nums_small, k_small)}")
Observable outcome: You can explain how βCode Example: Sliding Window Maximumβ changes the architecture or implementation path.
π System Outcome & Trade-offs
Observed User & Operator Behavior
User: Clients observe deterministic state without lost updates or race conditions.
Operator: Operators can verify stage outcomes against the scenario requirements.
Key Architectural Trade-offs
- System guarantees are traded against replication overhead and failover convergence times.
Key learnings
- Apply core-algorithm-patterns to Code Example: Sliding Window Maximum.
Apply Concepts from this Scenario
Test your understanding of the architecture decisions and trade-offs presented in this case study.
