function maxSlidingWindow(nums, k) {
const n = nums.length;
if (n === 0 || k === 0 || k > n) return [];
const result = [];
for (let i = 0; i <= n - k; i++) {
let maxVal = Number.NEGATIVE_INFINITY;
for (let j = i; j < i + k; j++) {
if (nums[j] > maxVal) maxVal = nums[j];
}
result.push(maxVal);
}
return result;
}
if (require && require.main === module) {
console.log(maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3));
console.log(maxSlidingWindow([1], 1));
console.log(maxSlidingWindow([1,-1], 1));
console.log(maxSlidingWindow([], 0));
console.log(maxSlidingWindow([5,4,3,2,1], 2));
console.log(maxSlidingWindow([1,2,3,4,5], 2));
console.log(maxSlidingWindow([2,2,2,2], 3));
}
// File: javascript.brute.js
// Brute-force Sliding Window Maximum
// O(n * k) time, O(1) extra space (n = nums.length)
/**
* Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
* This is straightforward and guarantees correctness by directly computing each window's max.
* However, it's inefficient for large n and k, as it performs redundant computations across overlapping windows.
*
* Why this solution:
* - It's simple and easy to implement without any additional data structures.
* - Serves as a baseline to verify the correctness of more optimized approaches during testing or debugging.
* - Useful for understanding the problem before optimizing.
*
* Where to use:
* - Small inputs where n and k are small (e.g., n <= 1000, k <= 1000).
* - In interviews to demonstrate initial understanding before moving to optimization.
* - When space is extremely constrained, though time may be a bottleneck.
*
* Complexity:
* - Time: O(n * k) - There are (n - k + 1) windows, and each takes O(k) time to find the max.
* For max constraints (n=10^5, k=10^5), this is 10^10 operations, which will timeout.
* - Space: O(1) - No extra space used besides the output array of size (n - k + 1).
*
* Interview notes:
* - Begin with this approach to show you grasp the problem: "We can naively scan each window to find the max, but it's O(nk)."
* - Explain why it's slow: "Overlapping windows recompute maxes unnecessarily; for large n, it's impractical."
* - Transition to optimized: "To improve, we can use a monotonic deque to track candidates in O(n) time."
* - Handles all cases correctly since it recomputes everything fresh for each window.
* - FAANG tip: Mention this first, then ask about constraints to justify optimization.
*
* Edge cases handled:
* - Empty array or k=0: Return empty array.
* - k=1: Returns the array itself, as each element is its own max.
* - k=n: Returns a single element, the global max.
* - All elements equal: Correctly returns the value for each window.
* - Decreasing sequence: Max is always the leftmost in window.
* - Increasing sequence: Max is always the rightmost.
* - Negatives and zeros: Max comparison handles <0 values correctly.
*
* @param {number[]} nums - the input array of integers
* @param {number} k - the sliding window size
* @returns {number[]} array of maximums for each window
*/
function maxSlidingWindow(nums, k) {
const n = nums.length;
if (n === 0 || k === 0 || k > n) return []; // Handle invalid or empty cases
const result = [];
for (let i = 0; i <= n - k; i++) { // Loop over each possible window start
let maxVal = Number.NEGATIVE_INFINITY; // Initialize to smallest possible
for (let j = i; j < i + k; j++) { // Scan the current window
if (nums[j] > maxVal) maxVal = nums[j]; // Update max
}
result.push(maxVal); // Add to result
}
return result;
}
// Test runner for Node.js environment
if (require && require.main === module) {
console.log(maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3)); // [3,3,5,5,6,7]
console.log(maxSlidingWindow([1], 1)); // [1]
console.log(maxSlidingWindow([1,-1], 1)); // [1,-1]
console.log(maxSlidingWindow([], 0)); // []
console.log(maxSlidingWindow([5,4,3,2,1], 2)); // [5,4,3,2]
console.log(maxSlidingWindow([1,2,3,4,5], 2)); // [2,3,4,5]
console.log(maxSlidingWindow([2,2,2,2], 3)); // [2,2]
}
function maxSlidingWindow(nums, k) {
const n = nums.length;
if (n === 0 || k === 0 || k > n) return [];
const deque = [];
const result = [];
for (let i = 0; i < n; i++) {
if (deque.length > 0 && deque[0] === i - k) {
deque.shift();
}
while (deque.length > 0 && nums[deque[deque.length - 1]] <= nums[i]) {
deque.pop();
}
deque.push(i);
if (i >= k - 1) {
result.push(nums[deque[0]]);
}
}
return result;
}
if (require && require.main === module) {
console.log(maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3));
console.log(maxSlidingWindow([1], 1));
console.log(maxSlidingWindow([1,-1], 1));
console.log(maxSlidingWindow([], 0));
console.log(maxSlidingWindow([5,4,3,2,1], 2));
console.log(maxSlidingWindow([1,2,3,4,5], 2));
console.log(maxSlidingWindow([2,2,2,2], 3));
}
// File: javascript.optimized.js
// Optimized Sliding Window Maximum using Monotonic Deque
// O(n) time, O(k) space
/**
* Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index of the max in the current window.
* For each new index i:
* - Remove front if it's out of the current window (i - k).
* - Remove back elements if they are smaller than nums[i] (they can't be max anymore).
* - Add i to the back.
* - If i >= k-1, add nums[front] to result.
* This way, each element is added and removed at most once, achieving O(n) time.
*
* Why this solution:
* - Avoids redundant computations by keeping only potential max candidates.
* - The deque ensures the front is the max, and back is maintained in decreasing order.
* - Superior to heap (O(n log k)) for large k.
*
* Where to use:
* - Large inputs (n up to 10^5, k up to 10^5) where brute-force would timeout.
* - Real-time streaming data for rolling max calculations.
* - Problems requiring efficient range queries for max/min.
*
* Complexity:
* - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per i).
* - Space: O(k) - Deque can grow to k in worst case (decreasing sequence).
*
* Interview notes:
* - Explain the monotonic property: "The deque stores indices in decreasing nums order, front is max."
* - Highlight why store indices: "To check if front is still in window (front >= i - k + 1)."
* - Discuss pop back: "If new nums[i] >= back, back can't be max in future windows containing i."
* - Compare to brute: "From O(nk) to O(n), crucial for constraints."
* - In JS, array as deque: push/pop O(1), shift O(n) worst, but total shifts <= n, so O(n) overall.
* - FAANG tip: Mention for min, use increasing deque (pop if nums[i] <= back).
*
* Edge cases handled:
* - Empty array or k=0: Return [].
* - k=1: Deque always has single index, result = nums.
* - k=n: Deque builds to hold the max index, single result.
* - Decreasing sequence: Deque size = k, front always the oldest (largest).
* - Increasing sequence: Frequent pops, deque size 1 (latest is max).
* - Duplicates: Pop if >= to keep the leftmost index for correct window timing.
* - Negatives/zeros: Comparisons handle <0 correctly.
* - All equal: With >= pop, deque size 1 (leftmost), pops as window slides.
*
* @param {number[]} nums - the input array of integers
* @param {number} k - the sliding window size
* @returns {number[]} array of maximums for each window
*/
function maxSlidingWindow(nums, k) {
const n = nums.length;
if (n === 0 || k === 0 || k > n) return [];
const deque = []; // Array as deque: shift front, pop/push back
const result = [];
for (let i = 0; i < n; i++) {
// Remove out-of-window from front
if (deque.length > 0 && deque[0] === i - k) {
deque.shift();
}
// Remove smaller (or equal for duplicates) from back
while (deque.length > 0 && nums[deque[deque.length - 1]] <= nums[i]) {
deque.pop();
}
// Add current index
deque.push(i);
// Add max to result if window is full
if (i >= k - 1) {
result.push(nums[deque[0]]);
}
}
return result;
}
// Test runner for Node.js environment
if (require && require.main === module) {
console.log(maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3)); // [3,3,5,5,6,7]
console.log(maxSlidingWindow([1], 1)); // [1]
console.log(maxSlidingWindow([1,-1], 1)); // [1,-1]
console.log(maxSlidingWindow([], 0)); // []
console.log(maxSlidingWindow([5,4,3,2,1], 2)); // [5,4,3,2]
console.log(maxSlidingWindow([1,2,3,4,5], 2)); // [2,3,4,5]
console.log(maxSlidingWindow([2,2,2,2], 3)); // [2,2]
}
"""
Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
This is straightforward and guarantees correctness by directly computing each window's max.
However, it's inefficient for large n and k, as it performs redundant computations across overlapping windows.
Why this solution:
- It's simple and easy to implement without any additional data structures.
- Serves as a baseline to verify the correctness of more optimized approaches during testing or debugging.
- Useful for understanding the problem before optimizing.
Where to use:
- Small inputs where n and k are small (e.g., n <= 1000, k <= 1000).
- In interviews to demonstrate initial understanding before moving to optimization.
- When space is extremely constrained, though time may be a bottleneck.
Complexity:
- Time: O(n * k) - There are (n - k + 1) windows, and each takes O(k) time to find the max.
For max constraints (n=10^5, k=10^5), this is 10^10 operations, which will timeout.
- Space: O(1) - No extra space used besides the output list of size (n - k + 1).
Interview notes:
- Begin with this approach to show you grasp the problem: "We can naively scan each window to find the max, but it's O(nk)."
- Explain why it's slow: "Overlapping windows recompute maxes unnecessarily; for large n, it's impractical."
- Transition to optimized: "To improve, we can use a monotonic deque to track candidates in O(n) time."
- Handles all cases correctly since it recomputes everything fresh for each window.
- FAANG tip: Mention this first, then ask about constraints to justify optimization.
Edge cases handled:
- Empty array or k=0: Return empty list.
- k=1: Returns the array itself, as each element is its own max.
- k=n: Returns a single element, the global max.
- All elements equal: Correctly returns the value for each window.
- Decreasing sequence: Max is always the leftmost in window.
- Increasing sequence: Max is always the rightmost.
- Negatives and zeros: Max comparison handles <0 values correctly.
"""
class Solution:
def maxSlidingWindow(self, nums: list[int], k: int) -> list[int]:
n = len(nums)
if n == 0 or k == 0 or k > n:
return []
result = []
for i in range(n - k + 1):
max_val = float('-inf')
for j in range(i, i + k):
if nums[j] > max_val:
max_val = nums[j]
result.append(max_val)
return result
if __name__ == "__main__":
sol = Solution()
print(sol.maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3))
print(sol.maxSlidingWindow([1], 1))
print(sol.maxSlidingWindow([1,-1], 1))
print(sol.maxSlidingWindow([], 0))
print(sol.maxSlidingWindow([5,4,3,2,1], 2))
print(sol.maxSlidingWindow([1,2,3,4,5], 2))
print(sol.maxSlidingWindow([2,2,2,2], 3))
# File: python.brute.py
# Brute-force Sliding Window Maximum
# O(n * k) time, O(1) extra space (n = len(nums))
"""
Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
This is straightforward and guarantees correctness by directly computing each window's max.
However, it's inefficient for large n and k, as it performs redundant computations across overlapping windows.
Why this solution:
- It's simple and easy to implement without any additional data structures.
- Serves as a baseline to verify the correctness of more optimized approaches during testing or debugging.
- Useful for understanding the problem before optimizing.
Where to use:
- Small inputs where n and k are small (e.g., n <= 1000, k <= 1000).
- In interviews to demonstrate initial understanding before moving to optimization.
- When space is extremely constrained, though time may be a bottleneck.
Complexity:
- Time: O(n * k) - There are (n - k + 1) windows, and each takes O(k) time to find the max.
For max constraints (n=10^5, k=10^5), this is 10^10 operations, which will timeout.
- Space: O(1) - No extra space used besides the output list of size (n - k + 1).
Interview notes:
- Begin with this approach to show you grasp the problem: "We can naively scan each window to find the max, but it's O(nk)."
- Explain why it's slow: "Overlapping windows recompute maxes unnecessarily; for large n, it's impractical."
- Transition to optimized: "To improve, we can use a monotonic deque to track candidates in O(n) time."
- Handles all cases correctly since it recomputes everything fresh for each window.
- FAANG tip: Mention this first, then ask about constraints to justify optimization.
Edge cases handled:
- Empty array or k=0: Return empty list.
- k=1: Returns the array itself, as each element is its own max.
- k=n: Returns a single element, the global max.
- All elements equal: Correctly returns the value for each window.
- Decreasing sequence: Max is always the leftmost in window.
- Increasing sequence: Max is always the rightmost.
- Negatives and zeros: Max comparison handles <0 values correctly.
"""
class Solution:
def maxSlidingWindow(self, nums: list[int], k: int) -> list[int]:
n = len(nums)
if n == 0 or k == 0 or k > n:
return [] # Handle invalid or empty cases
result = []
for i in range(n - k + 1): # Loop over each possible window start
max_val = float('-inf') # Initialize to smallest possible
for j in range(i, i + k): # Scan the current window
if nums[j] > max_val:
max_val = nums[j] # Update max
result.append(max_val) # Add to result
return result
# Test runner
if __name__ == "__main__":
sol = Solution()
print(sol.maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3)) # [3,3,5,5,6,7]
print(sol.maxSlidingWindow([1], 1)) # [1]
print(sol.maxSlidingWindow([1,-1], 1)) # [1,-1]
print(sol.maxSlidingWindow([], 0)) # []
print(sol.maxSlidingWindow([5,4,3,2,1], 2)) # [5,4,3,2]
print(sol.maxSlidingWindow([1,2,3,4,5], 2)) # [2,3,4,5]
print(sol.maxSlidingWindow([2,2,2,2], 3)) # [2,2]
"""
Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index of the max in the current window.
For each new index i:
- Remove front if it's out of the current window (i - k).
- Remove back elements if they are smaller than or equal to nums[i] (they can't be max anymore, and equals to keep leftmost).
- Add i to the back.
- If i >= k-1, append nums[front] to result.
This way, each element is added and removed at most once, achieving O(n) time.
Why this solution:
- Efficient for large n, avoids O(k) per window.
- The deque maintains potential max candidates, discarding useless ones.
- Superior to heap (O(n log k)) for large k.
Where to use:
- Large inputs (n up to 10^5, k up to 10^5) where brute-force would timeout.
- Real-time streaming data for rolling max calculations.
- Problems requiring efficient range queries for max/min.
Complexity:
- Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per i).
- Space: O(k) - Deque can grow to k in worst case (decreasing sequence).
Interview notes:
- Explain the monotonic property: "The deque stores indices in decreasing nums order, front is max."
- Highlight why store indices: "To check if front is still in window (front >= i - k + 1)."
- Discuss pop back: "If new nums[i] >= back, back can't be max in future windows containing i; >= handles duplicates by keeping leftmost."
- Compare to brute: "From O(nk) to O(n), crucial for constraints."
- In Python, list as deque: append/pop O(1), popleft O(n) worst, but total O(n); use collections.deque for O(1) all.
- FAANG tip: Mention for min, use increasing deque (pop if nums[i] <= back).
Edge cases handled:
- Empty array or k=0: Return [].
- k=1: Deque always single, result = nums.
- k=n: Deque builds to hold the max index, single result.
- Decreasing sequence: Deque size = k, front always oldest (largest).
- Increasing sequence: Frequent pops, deque size 1 (latest is max).
- Duplicates: Pop if >= to keep leftmost index.
- Negatives/zeros: Comparisons handle <0 correctly.
- All equal: With >= pop, deque size 1 (leftmost), pops as window slides.
"""
from typing import List
from collections import deque
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
n = len(nums)
if n == 0 or k == 0 or k > n:
return []
deq = deque()
result = []
for i in range(n):
if deq and deq[0] == i - k:
deq.popleft()
while deq and nums[deq[-1]] <= nums[i]:
deq.pop()
deq.append(i)
if i >= k - 1:
result.append(nums[deq[0]])
return result
if __name__ == "__main__":
sol = Solution()
print(sol.maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3))
print(sol.maxSlidingWindow([1], 1))
print(sol.maxSlidingWindow([1,-1], 1))
print(sol.maxSlidingWindow([], 0))
print(sol.maxSlidingWindow([5,4,3,2,1], 2))
print(sol.maxSlidingWindow([1,2,3,4,5], 2))
print(sol.maxSlidingWindow([2,2,2,2], 3))
# File: python.optimized.py
# Optimized Sliding Window Maximum using Monotonic Deque
# O(n) time, O(k) space
"""
Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index of the max in the current window.
For each new index i:
- Remove front if it's out of the current window (i - k).
- Remove back elements if they are smaller than or equal to nums[i] (they can't be max anymore, and equals to keep leftmost).
- Add i to the back.
- If i >= k-1, append nums[front] to result.
This way, each element is added and removed at most once, achieving O(n) time.
Why this solution:
- Efficient for large n, avoids O(k) per window.
- The deque maintains potential max candidates, discarding useless ones.
- Superior to heap (O(n log k)) for large k.
Where to use:
- Large inputs (n up to 10^5, k up to 10^5) where brute-force would timeout.
- Real-time streaming data for rolling max calculations.
- Problems requiring efficient range queries for max/min.
Complexity:
- Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per i).
- Space: O(k) - Deque can grow to k in worst case (decreasing sequence).
Interview notes:
- Explain the monotonic property: "The deque stores indices in decreasing nums order, front is max."
- Highlight why store indices: "To check if front is still in window (front >= i - k + 1)."
- Discuss pop back: "If new nums[i] >= back, back can't be max in future windows containing i; >= handles duplicates by keeping leftmost."
- Compare to brute: "From O(nk) to O(n), crucial for constraints."
- In Python, list as deque: append/pop O(1), popleft O(n) worst, but total O(n); use collections.deque for O(1) all.
- FAANG tip: Mention for min, use increasing deque (pop if nums[i] <= back).
Edge cases handled:
- Empty array or k=0: Return [].
- k=1: Deque always single, result = nums.
- k=n: Deque builds to hold the max index, single result.
- Decreasing sequence: Deque size = k, front always oldest (largest).
- Increasing sequence: Frequent pops, deque size 1 (latest is max).
- Duplicates: Pop if >= to keep leftmost index.
- Negatives/zeros: Comparisons handle <0 correctly.
- All equal: With >= pop, deque size 1 (leftmost), pops as window slides.
"""
from typing import List
from collections import deque # For O(1) popleft
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
n = len(nums)
if n == 0 or k == 0 or k > n:
return []
deq = deque() # Deque of indices
result = []
for i in range(n):
# Remove out-of-window from front
if deq and deq[0] == i - k:
deq.popleft()
# Remove smaller or equal from back
while deq and nums[deq[-1]] <= nums[i]:
deq.pop()
# Add current index
deq.append(i)
# Add max to result if window full
if i >= k - 1:
result.append(nums[deq[0]])
return result
# Test runner
if __name__ == "__main__":
sol = Solution()
print(sol.maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3)) # [3,3,5,5,6,7]
print(sol.maxSlidingWindow([1], 1)) # [1]
print(sol.maxSlidingWindow([1,-1], 1)) # [1,-1]
print(sol.maxSlidingWindow([], 0)) # []
print(sol.maxSlidingWindow([5,4,3,2,1], 2)) # [5,4,3,2]
print(sol.maxSlidingWindow([1,2,3,4,5], 2)) # [2,3,4,5]
print(sol.maxSlidingWindow([2,2,2,2], 3)) # [2,2]
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
if (n == 0 || k == 0 || k > n) return new int[0];
int[] result = new int[n - k + 1];
for (int i = 0; i <= n - k; i++) {
int maxVal = Integer.MIN_VALUE;
for (int j = i; j < i + k; j++) {
maxVal = Math.max(maxVal, nums[j]);
}
result[i] = maxVal;
}
return result;
}
}
class Main {
public static void main(String[] args) {
Solution sol = new Solution();
int[] nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
int[] nums2 = {1};
int[] nums3 = {1, -1};
int[] nums4 = {5, 4, 3, 2, 1};
int[] nums5 = {1, 2, 3, 4, 5};
int[] nums6 = {2, 2, 2, 2};
int[] nums7 = {};
print(sol.maxSlidingWindow(nums1, 3));
print(sol.maxSlidingWindow(nums2, 1));
print(sol.maxSlidingWindow(nums3, 1));
print(sol.maxSlidingWindow(nums7, 0));
print(sol.maxSlidingWindow(nums4, 2));
print(sol.maxSlidingWindow(nums5, 2));
print(sol.maxSlidingWindow(nums6, 3));
}
private static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
if (i < arr.length - 1) System.out.print(",");
}
System.out.println("]");
}
}
// File: java.brute.java
// Brute-force Sliding Window Maximum
// O(n * k) time, O(1) extra space (n = nums.length)
/**
* Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
* This approach directly computes the max for each window, ensuring correctness but suffering
* from high time complexity due to redundant computations across overlapping windows.
*
* Why this solution:
* - Straightforward, no additional data structures required.
* - Useful as a starting point in interviews to explain the problem before optimizing.
* - Guarantees correctness for all inputs by recomputing each window's max.
*
* Where to use:
* - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
* - To validate optimized solutions during development or debugging.
* - When space is a critical constraint.
*
* Complexity:
* - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
* For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
* - Space: O(1) - Excluding output array, only uses a few variables.
*
* Interview notes:
* - Start with this to show problem understanding: "We can scan each window to find the max, but it's O(nk)."
* - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's too slow."
* - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
* - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
* - Handles negatives, zeros, and duplicates correctly since it recomputes fresh.
*
* Edge cases handled:
* - Empty array or k=0: Returns empty array.
* - k=1: Returns the input array, as each element is its own max.
* - k=n: Returns a single element, the global max.
* - Decreasing sequence: Max is always leftmost in window.
* - Increasing sequence: Max is always rightmost.
* - All equal elements: Returns the same value for all windows.
* - Negatives/zeros: Comparisons handle all values correctly.
*
* @param nums Input array of integers
* @param k Sliding window size
* @return Array of maximums for each window
*/
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
if (n == 0 || k == 0 || k > n) return new int[0]; // Handle invalid/empty cases
int[] result = new int[n - k + 1];
for (int i = 0; i <= n - k; i++) { // Loop over window starts
int maxVal = Integer.MIN_VALUE; // Initialize to smallest possible
for (int j = i; j < i + k; j++) { // Scan window
maxVal = Math.max(maxVal, nums[j]); // Update max
}
result[i] = maxVal; // Add to result
}
return result;
}
}
// Test runner
class Main {
public static void main(String[] args) {
Solution sol = new Solution();
int[] nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
int[] nums2 = {1};
int[] nums3 = {1, -1};
int[] nums4 = {5, 4, 3, 2, 1};
int[] nums5 = {1, 2, 3, 4, 5};
int[] nums6 = {2, 2, 2, 2};
int[] nums7 = {};
print(sol.maxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
print(sol.maxSlidingWindow(nums2, 1)); // [1]
print(sol.maxSlidingWindow(nums3, 1)); // [1,-1]
print(sol.maxSlidingWindow(nums7, 0)); // []
print(sol.maxSlidingWindow(nums4, 2)); // [5,4,3,2]
print(sol.maxSlidingWindow(nums5, 2)); // [2,3,4,5]
print(sol.maxSlidingWindow(nums6, 3)); // [2,2]
}
private static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
if (i < arr.length - 1) System.out.print(",");
}
System.out.println("]");
}
}
import java.util.ArrayDeque;
import java.util.Deque;
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
if (n == 0 || k == 0 || k > n) return new int[0];
Deque<Integer> deq = new ArrayDeque<>();
int[] result = new int[n - k + 1];
int index = 0;
for (int i = 0; i < n; i++) {
if (!deq.isEmpty() && deq.peekFirst() == i - k) {
deq.pollFirst();
}
while (!deq.isEmpty() && nums[deq.peekLast()] <= nums[i]) {
deq.pollLast();
}
deq.offerLast(i);
if (i >= k - 1) {
result[index++] = nums[deq.peekFirst()];
}
}
return result;
}
}
class Main {
public static void main(String[] args) {
Solution sol = new Solution();
int[] nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
int[] nums2 = {1};
int[] nums3 = {1, -1};
int[] nums4 = {5, 4, 3, 2, 1};
int[] nums5 = {1, 2, 3, 4, 5};
int[] nums6 = {2, 2, 2, 2};
int[] nums7 = {};
print(sol.maxSlidingWindow(nums1, 3));
print(sol.maxSlidingWindow(nums2, 1));
print(sol.maxSlidingWindow(nums3, 1));
print(sol.maxSlidingWindow(nums7, 0));
print(sol.maxSlidingWindow(nums4, 2));
print(sol.maxSlidingWindow(nums5, 2));
print(sol.maxSlidingWindow(nums6, 3));
}
private static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
if (i < arr.length - 1) System.out.print(",");
}
System.out.println("]");
}
}
// File: java.optimized.java
// Optimized Sliding Window Maximum using Monotonic Deque
// O(n) time, O(k) space
/**
* Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
* of the max in the current window. For each index i:
* - Pop front if index is out of current window (i - k).
* - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
* - Push i to back.
* - If i >= k-1, add nums[front] to result (current window max).
* This ensures O(n) time as each element is pushed/popped at most once.
*
* Why this solution:
* - Eliminates redundant max computations by maintaining only potential max candidates.
* - Deque ensures front is the max, and back is kept in decreasing order.
* - Outperforms max-heap (O(n log k)) for large inputs.
*
* Where to use:
* - Large inputs (n,k up to 10^5) where O(nk) would timeout.
* - Streaming data scenarios requiring real-time max in fixed windows.
* - When O(n) time is critical for performance.
*
* Complexity:
* - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
* - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
*
* Interview notes:
* - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
* - Why indices: "To check if front index is in window [i-k+1, i]."
* - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
* - Compare to brute: "O(nk) to O(n), critical for n=10^5."
* - Java advantage: ArrayDeque provides O(1) front/back operations.
* - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
* - Amortized analysis: "Each index in/out once, total 2n operations."
*
* Edge cases handled:
* - Empty array or k=0: Return empty array.
* - k=1: Each element is max, deque size 1 per iteration.
* - k=n: Single window, deque builds to global max.
* - Decreasing sequence: Deque grows to k, front always largest.
* - Increasing sequence: Frequent pops, deque often size 1 (latest max).
* - Duplicates: Pop if >= to keep leftmost index for correct window timing.
* - Negatives/zeros: Comparisons handle all values correctly.
* - All equal elements: Pop >= keeps deque size 1, slides correctly.
*
* @param nums Input array of integers
* @param k Sliding window size
* @return Array of maximums for each window
*/
import java.util.ArrayDeque;
import java.util.Deque;
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
if (n == 0 || k == 0 || k > n) return new int[0];
Deque<Integer> deq = new ArrayDeque<>();
int[] result = new int[n - k + 1];
int index = 0;
for (int i = 0; i < n; i++) {
// Remove out-of-window front
if (!deq.isEmpty() && deq.peekFirst() == i - k) {
deq.pollFirst();
}
// Remove smaller or equal from back
while (!deq.isEmpty() && nums[deq.peekLast()] <= nums[i]) {
deq.pollLast();
}
// Add current index
deq.offerLast(i);
// Add max to result if window is full
if (i >= k - 1) {
result[index++] = nums[deq.peekFirst()];
}
}
return result;
}
}
// Test runner
class Main {
public static void main(String[] args) {
Solution sol = new Solution();
int[] nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
int[] nums2 = {1};
int[] nums3 = {1, -1};
int[] nums4 = {5, 4, 3, 2, 1};
int[] nums5 = {1, 2, 3, 4, 5};
int[] nums6 = {2, 2, 2, 2};
int[] nums7 = {};
print(sol.maxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
print(sol.maxSlidingWindow(nums2, 1)); // [1]
print(sol.maxSlidingWindow(nums3, 1)); // [1,-1]
print(sol.maxSlidingWindow(nums7, 0)); // []
print(sol.maxSlidingWindow(nums4, 2)); // [5,4,3,2]
print(sol.maxSlidingWindow(nums5, 2)); // [2,3,4,5]
print(sol.maxSlidingWindow(nums6, 3)); // [2,2]
}
private static void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
if (i < arr.length - 1) System.out.print(",");
}
System.out.println("]");
}
}
#include <vector>
#include <climits>
class Solution {
public:
std::vector<int> maxSlidingWindow(std::vector<int>& nums, int k) {
int n = nums.size();
if (n == 0 || k == 0 || k > n) return {};
std::vector<int> result;
for (int i = 0; i <= n - k; ++i) {
int max_val = INT_MIN;
for (int j = i; j < i + k; ++j) {
max_val = std::max(max_val, nums[j]);
}
result.push_back(max_val);
}
return result;
}
};
#ifdef LOCAL_TEST
#include <iostream>
int main() {
Solution sol;
std::vector<int> nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
std::vector<int> nums2 = {1};
std::vector<int> nums3 = {1, -1};
std::vector<int> nums4 = {5, 4, 3, 2, 1};
std::vector<int> nums5 = {1, 2, 3, 4, 5};
std::vector<int> nums6 = {2, 2, 2, 2};
std::vector<int> nums7 = {};
auto print = [](const std::vector<int>& vec) {
std::cout << "[";
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i];
if (i < vec.size() - 1) std::cout << ",";
}
std::cout << "]" << std::endl;
};
print(sol.maxSlidingWindow(nums1, 3));
print(sol.maxSlidingWindow(nums2, 1));
print(sol.maxSlidingWindow(nums3, 1));
print(sol.maxSlidingWindow(nums7, 0));
print(sol.maxSlidingWindow(nums4, 2));
print(sol.maxSlidingWindow(nums5, 2));
print(sol.maxSlidingWindow(nums6, 3));
return 0;
}
#endif
// File: cpp.brute.cpp
// Brute-force Sliding Window Maximum
// O(n * k) time, O(1) extra space (n = nums.size())
/**
* Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
* This approach directly computes the max for each window by scanning, ensuring correctness but suffering
* from high time complexity due to redundant computations across overlapping windows.
*
* Why this solution:
* - Simplest implementation, no additional data structures needed.
* - Ideal for explaining the problem's requirements in interviews before optimizing.
* - Guarantees correctness for all inputs by recomputing max for each window.
*
* Where to use:
* - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
* - As a baseline to validate optimized solutions during development or debugging.
* - When space is critical and time is not a constraint.
*
* Complexity:
* - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
* For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
* - Space: O(1) - Excluding output vector, only uses a few variables.
*
* Interview notes:
* - Start with this to demonstrate problem understanding: "We can scan each window to find the max."
* - Highlight inefficiency: "For n=10^5, k=10^5, it’s too slow due to repeated scans."
* - Transition to optimized: "A monotonic deque can reduce this to O(n) by tracking max candidates."
* - FAANG tip: Use this to set up discussion, then pivot to deque for constraints like n=10^5.
* - Handles negatives, zeros, and duplicates correctly since it recomputes each window.
*
* Edge cases handled:
* - Empty array or k=0: Returns empty vector.
* - k=1: Returns the input array, as each element is its own max.
* - k=n: Returns a single element, the global max.
* - Decreasing sequence: Max is always leftmost in window.
* - Increasing sequence: Max is always rightmost.
* - All equal elements: Returns the same value for all windows.
* - Negatives/zeros: Comparisons handle all values correctly.
*
* @param nums Input vector of integers
* @param k Sliding window size
* @return Vector of maximums for each window
*/
#include <vector>
#include <climits>
class Solution {
public:
std::vector<int> maxSlidingWindow(std::vector<int>& nums, int k) {
int n = nums.size();
if (n == 0 || k == 0 || k > n) return {}; // Handle invalid/empty cases
std::vector<int> result;
for (int i = 0; i <= n - k; ++i) { // Loop over window starts
int max_val = INT_MIN; // Initialize to smallest possible
for (int j = i; j < i + k; ++j) { // Scan window
max_val = std::max(max_val, nums[j]); // Update max
}
result.push_back(max_val); // Add to result
}
return result;
}
};
// Test runner
#ifdef LOCAL_TEST
#include <iostream>
int main() {
Solution sol;
std::vector<int> nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
std::vector<int> nums2 = {1};
std::vector<int> nums3 = {1, -1};
std::vector<int> nums4 = {5, 4, 3, 2, 1};
std::vector<int> nums5 = {1, 2, 3, 4, 5};
std::vector<int> nums6 = {2, 2, 2, 2};
std::vector<int> nums7 = {};
auto print = [](const std::vector<int>& vec) {
std::cout << "[";
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i];
if (i < vec.size() - 1) std::cout << ",";
}
std::cout << "]" << std::endl;
};
print(sol.maxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
print(sol.maxSlidingWindow(nums2, 1)); // [1]
print(sol.maxSlidingWindow(nums3, 1)); // [1,-1]
print(sol.maxSlidingWindow(nums7, 0)); // []
print(sol.maxSlidingWindow(nums4, 2)); // [5,4,3,2]
print(sol.maxSlidingWindow(nums5, 2)); // [2,3,4,5]
print(sol.maxSlidingWindow(nums6, 3)); // [2,2]
return 0;
}
#endif
#include <vector>
#include <deque>
class Solution {
public:
std::vector<int> maxSlidingWindow(std::vector<int>& nums, int k) {
int n = nums.size();
if (n == 0 || k == 0 || k > n) return {};
std::deque<int> deq;
std::vector<int> result;
for (int i = 0; i < n; ++i) {
if (!deq.empty() && deq.front() == i - k) {
deq.pop_front();
}
while (!deq.empty() && nums[deq.back()] <= nums[i]) {
deq.pop_back();
}
deq.push_back(i);
if (i >= k - 1) {
result.push_back(nums[deq.front()]);
}
}
return result;
}
};
#ifdef LOCAL_TEST
#include <iostream>
int main() {
Solution sol;
std::vector<int> nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
std::vector<int> nums2 = {1};
std::vector<int> nums3 = {1, -1};
std::vector<int> nums4 = {5, 4, 3, 2, 1};
std::vector<int> nums5 = {1, 2, 3, 4, 5};
std::vector<int> nums6 = {2, 2, 2, 2};
std::vector<int> nums7 = {};
auto print = [](const std::vector<int>& vec) {
std::cout << "[";
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i];
if (i < vec.size() - 1) std::cout << ",";
}
std::cout << "]" << std::endl;
};
print(sol.maxSlidingWindow(nums1, 3));
print(sol.maxSlidingWindow(nums2, 1));
print(sol.maxSlidingWindow(nums3, 1));
print(sol.maxSlidingWindow(nums7, 0));
print(sol.maxSlidingWindow(nums4, 2));
print(sol.maxSlidingWindow(nums5, 2));
print(sol.maxSlidingWindow(nums6, 3));
return 0;
}
#endif
// File: cpp.optimized.cpp
// Optimized Sliding Window Maximum using Monotonic Deque
// O(n) time, O(k) space
/**
* Optimized approach: Use a monotonic decreasing deque to store indices of potential max candidates.
* For each index i:
* - Pop front if index is out of current window (i - k).
* - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
* - Push i to back.
* - If i >= k-1, add nums[front] to result (current window max).
* This ensures O(n) time as each element is pushed/popped at most once.
*
* Why this solution:
* - Eliminates redundant max computations by maintaining a deque of candidates.
* - Front of deque always holds the current window's max index.
* - Beats max-heap (O(n log k)) for large inputs.
*
* Where to use:
* - Large inputs (n,k up to 10^5) where O(nk) would timeout.
* - Streaming data scenarios requiring real-time max in fixed windows.
* - When O(n) time is critical for performance.
*
* Complexity:
* - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
* - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
*
* Interview notes:
* - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
* - Why indices: "To check if front index is in window [i-k+1, i]."
* - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
* - Compare to brute: "O(nk) to O(n), critical for n=10^5."
* - C++ advantage: std::deque provides O(1) front/back operations, unlike JS array shift().
* - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
* - Amortized analysis: "Each index in/out once, total 2n operations."
*
* Edge cases handled:
* - Empty array or k=0: Return empty vector.
* - k=1: Each element is max, deque size 1 per iteration.
* - k=n: Single window, deque builds to global max.
* - Decreasing sequence: Deque grows to k, front always largest.
* - Increasing sequence: Frequent pops, deque often size 1 (latest max).
* - Duplicates: Pop if >= to keep leftmost index for correct window timing.
* - Negatives/zeros: Comparisons handle all values correctly.
* - All equal elements: Pop >= keeps deque size 1, slides correctly.
*
* @param nums Input vector of integers
* @param k Sliding window size
* @return Vector of maximums for each window
*/
#include <vector>
#include <deque>
class Solution {
public:
std::vector<int> maxSlidingWindow(std::vector<int>& nums, int k) {
int n = nums.size();
if (n == 0 || k == 0 || k > n) return {};
std::deque<int> deq; // Deque of indices
std::vector<int> result;
for (int i = 0; i < n; ++i) {
// Remove out-of-window front
if (!deq.empty() && deq.front() == i - k) {
deq.pop_front();
}
// Remove smaller or equal from back
while (!deq.empty() && nums[deq.back()] <= nums[i]) {
deq.pop_back();
}
// Add current index
deq.push_back(i);
// Add max to result if window is full
if (i >= k - 1) {
result.push_back(nums[deq.front()]);
}
}
return result;
}
};
// Test runner
#ifdef LOCAL_TEST
#include <iostream>
int main() {
Solution sol;
std::vector<int> nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
std::vector<int> nums2 = {1};
std::vector<int> nums3 = {1, -1};
std::vector<int> nums4 = {5, 4, 3, 2, 1};
std::vector<int> nums5 = {1, 2, 3, 4, 5};
std::vector<int> nums6 = {2, 2, 2, 2};
std::vector<int> nums7 = {};
auto print = [](const std::vector<int>& vec) {
std::cout << "[";
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i];
if (i < vec.size() - 1) std::cout << ",";
}
std::cout << "]" << std::endl;
};
print(sol.maxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
print(sol.maxSlidingWindow(nums2, 1)); // [1]
print(sol.maxSlidingWindow(nums3, 1)); // [1,-1]
print(sol.maxSlidingWindow(nums7, 0)); // []
print(sol.maxSlidingWindow(nums4, 2)); // [5,4,3,2]
print(sol.maxSlidingWindow(nums5, 2)); // [2,3,4,5]
print(sol.maxSlidingWindow(nums6, 3)); // [2,2]
return 0;
}
#endif
public class Solution {
public int[] MaxSlidingWindow(int[] nums, int k) {
int n = nums.Length;
if (n == 0 || k == 0 || k > n) return new int[0];
int[] result = new int[n - k + 1];
for (int i = 0; i <= n - k; i++) {
int maxVal = int.MinValue;
for (int j = i; j < i + k; j++) {
maxVal = Math.Max(maxVal, nums[j]);
}
result[i] = maxVal;
}
return result;
}
}
class Program {
static void Main(string[] args) {
Solution sol = new Solution();
int[] nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
int[] nums2 = {1};
int[] nums3 = {1, -1};
int[] nums4 = {5, 4, 3, 2, 1};
int[] nums5 = {1, 2, 3, 4, 5};
int[] nums6 = {2, 2, 2, 2};
int[] nums7 = {};
void Print(int[] arr) {
Console.Write("[");
for (int i = 0; i < arr.Length; i++) {
Console.Write(arr[i]);
if (i < arr.Length - 1) Console.Write(",");
}
Console.WriteLine("]");
}
Print(sol.MaxSlidingWindow(nums1, 3));
Print(sol.MaxSlidingWindow(nums2, 1));
Print(sol.MaxSlidingWindow(nums3, 1));
Print(sol.MaxSlidingWindow(nums7, 0));
Print(sol.MaxSlidingWindow(nums4, 2));
Print(sol.MaxSlidingWindow(nums5, 2));
Print(sol.MaxSlidingWindow(nums6, 3));
}
}
// File: csharp.brute.cs
// Brute-force Sliding Window Maximum
// O(n * k) time, O(1) extra space (n = nums.Length)
/**
* Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
* This approach directly computes the max for each window by scanning, ensuring correctness but suffering
* from high time complexity due to redundant computations across overlapping windows.
*
* Why this solution:
* - Simplest implementation, requiring no additional data structures beyond the output array.
* - Ideal for interviews to demonstrate initial problem understanding before optimization.
* - Guarantees correctness for all inputs by recomputing max for each window.
*
* Where to use:
* - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
* - To validate optimized solutions during development or debugging.
* - When space is critical, though time complexity may cause timeouts for large inputs.
*
* Complexity:
* - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
* For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
* - Space: O(1) - Excluding output array, only uses a few variables.
*
* Interview notes:
* - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
* - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
* - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
* - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
* - Handles all cases correctly since it recomputes fresh for each window.
*
* Edge cases handled:
* - Empty array or k=0: Returns empty array.
* - k=1: Returns the input array, as each element is its own max.
* - k=n: Returns a single element, the global max.
* - Decreasing sequence: Max is always leftmost in window.
* - Increasing sequence: Max is always rightmost.
* - All equal elements: Returns the same value for all windows.
* - Negatives/zeros: Comparisons handle all values correctly.
*
* ASCII Example for nums=[1,3,-1], k=3:
* Window 0-2: [1,3,-1]
* - Scan: 1, 3 (max so far), -1 < 3 → max = 3
* Output: [3]
*
* ASCII for nums=[5,4,3], k=2:
* Window 0-1: [5,4] → max = 5
* Window 1-2: [4,3] → max = 4
* Output: [5,4]
*
* @param nums Input array of integers
* @param k Sliding window size
* @return Array of maximums for each window
*/
public class Solution {
public int[] MaxSlidingWindow(int[] nums, int k) {
int n = nums.Length;
if (n == 0 || k == 0 || k > n) return new int[0]; // Handle invalid/empty cases
int[] result = new int[n - k + 1];
for (int i = 0; i <= n - k; i++) { // Loop over window starts
int maxVal = int.MinValue; // Initialize to smallest possible
for (int j = i; j < i + k; j++) { // Scan window
maxVal = Math.Max(maxVal, nums[j]); // Update max
}
result[i] = maxVal; // Add to result
}
return result;
}
}
// Test runner
class Program {
static void Main(string[] args) {
Solution sol = new Solution();
int[] nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
int[] nums2 = {1};
int[] nums3 = {1, -1};
int[] nums4 = {5, 4, 3, 2, 1};
int[] nums5 = {1, 2, 3, 4, 5};
int[] nums6 = {2, 2, 2, 2};
int[] nums7 = {};
void Print(int[] arr) {
Console.Write("[");
for (int i = 0; i < arr.Length; i++) {
Console.Write(arr[i]);
if (i < arr.Length - 1) Console.Write(",");
}
Console.WriteLine("]");
}
Print(sol.MaxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
Print(sol.MaxSlidingWindow(nums2, 1)); // [1]
Print(sol.MaxSlidingWindow(nums3, 1)); // [1,-1]
Print(sol.MaxSlidingWindow(nums7, 0)); // []
Print(sol.MaxSlidingWindow(nums4, 2)); // [5,4,3,2]
Print(sol.MaxSlidingWindow(nums5, 2)); // [2,3,4,5]
Print(sol.MaxSlidingWindow(nums6, 3)); // [2,2]
}
}
using System.Collections.Generic;
public class Solution {
public int[] MaxSlidingWindow(int[] nums, int k) {
int n = nums.Length;
if (n == 0 || k == 0 || k > n) return new int[0];
LinkedList<int> deq = new LinkedList<int>();
int[] result = new int[n - k + 1];
int index = 0;
for (int i = 0; i < n; i++) {
if (deq.Count > 0 && deq.First.Value == i - k) {
deq.RemoveFirst();
}
while (deq.Count > 0 && nums[deq.Last.Value] <= nums[i]) {
deq.RemoveLast();
}
deq.AddLast(i);
if (i >= k - 1) {
result[index++] = nums[deq.First.Value];
}
}
return result;
}
}
class Program {
static void Main(string[] args) {
Solution sol = new Solution();
int[] nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
int[] nums2 = {1};
int[] nums3 = {1, -1};
int[] nums4 = {5, 4, 3, 2, 1};
int[] nums5 = {1, 2, 3, 4, 5};
int[] nums6 = {2, 2, 2, 2};
int[] nums7 = {};
void Print(int[] arr) {
Console.Write("[");
for (int i = 0; i < arr.Length; i++) {
Console.Write(arr[i]);
if (i < arr.Length - 1) Console.Write(",");
}
Console.WriteLine("]");
}
Print(sol.MaxSlidingWindow(nums1, 3));
Print(sol.MaxSlidingWindow(nums2, 1));
Print(sol.MaxSlidingWindow(nums3, 1));
Print(sol.MaxSlidingWindow(nums7, 0));
Print(sol.MaxSlidingWindow(nums4, 2));
Print(sol.MaxSlidingWindow(nums5, 2));
Print(sol.MaxSlidingWindow(nums6, 3));
}
}
// File: csharp.optimized.cs
// Optimized Sliding Window Maximum using Monotonic Deque
// O(n) time, O(k) space
/**
* Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
* of the max in the current window. For each index i:
* - Pop front if index is out of current window (i - k).
* - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
* - Push i to back.
* - If i >= k-1, add nums[front] to result (current window max).
* This ensures O(n) time as each element is pushed/popped at most once.
*
* Why this solution:
* - Eliminates redundant max computations by maintaining only potential max candidates.
* - Deque ensures front is the max, and back is kept in decreasing order.
* - Outperforms max-heap (O(n log k)) for large inputs.
*
* Where to use:
* - Large inputs (n,k up to 10^5) where O(nk) would timeout.
* - Streaming data scenarios requiring real-time max in fixed windows.
* - When O(n) time is critical for performance.
*
* Complexity:
* - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
* - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
*
* Interview notes:
* - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
* - Why indices: "To check if front index is in window [i-k+1, i]."
* - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
* - Compare to brute: "O(nk) to O(n), critical for n=10^5."
* - C# advantage: Deque (LinkedList) provides O(1) front/back operations.
* - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
* - Amortized analysis: "Each index in/out once, total 2n operations."
*
* Edge cases handled:
* - Empty array or k=0: Return empty array.
* - k=1: Each element is max, deque size 1 per iteration.
* - k=n: Single window, deque builds to global max.
* - Decreasing sequence: Deque grows to k, front always largest.
* - Increasing sequence: Frequent pops, deque often size 1 (latest max).
* - Duplicates: Pop if >= to keep leftmost index for correct window timing.
* - Negatives/zeros: Comparisons handle all values correctly.
* - All equal elements: Pop >= keeps deque size 1, slides correctly.
*
* ASCII Example for nums=[1,3,-1,-3,5], k=3:
* i=0: deq=[] → push 0 → deq=[0] (1)
* i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
* i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
* i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
* i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
*
* @param nums Input array of integers
* @param k Sliding window size
* @return Array of maximums for each window
*/
using System.Collections.Generic;
public class Solution {
public int[] MaxSlidingWindow(int[] nums, int k) {
int n = nums.Length;
if (n == 0 || k == 0 || k > n) return new int[0];
LinkedList<int> deq = new LinkedList<int>(); // Deque of indices
int[] result = new int[n - k + 1];
int index = 0;
for (int i = 0; i < n; i++) {
// Remove out-of-window front
if (deq.Count > 0 && deq.First.Value == i - k) {
deq.RemoveFirst();
}
// Remove smaller or equal from back
while (deq.Count > 0 && nums[deq.Last.Value] <= nums[i]) {
deq.RemoveLast();
}
// Add current index
deq.AddLast(i);
// Add max to result if window is full
if (i >= k - 1) {
result[index++] = nums[deq.First.Value];
}
}
return result;
}
}
// Test runner
class Program {
static void Main(string[] args) {
Solution sol = new Solution();
int[] nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
int[] nums2 = {1};
int[] nums3 = {1, -1};
int[] nums4 = {5, 4, 3, 2, 1};
int[] nums5 = {1, 2, 3, 4, 5};
int[] nums6 = {2, 2, 2, 2};
int[] nums7 = {};
void Print(int[] arr) {
Console.Write("[");
for (int i = 0; i < arr.Length; i++) {
Console.Write(arr[i]);
if (i < arr.Length - 1) Console.Write(",");
}
Console.WriteLine("]");
}
Print(sol.MaxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
Print(sol.MaxSlidingWindow(nums2, 1)); // [1]
Print(sol.MaxSlidingWindow(nums3, 1)); // [1,-1]
Print(sol.MaxSlidingWindow(nums7, 0)); // []
Print(sol.MaxSlidingWindow(nums4, 2)); // [5,4,3,2]
Print(sol.MaxSlidingWindow(nums5, 2)); // [2,3,4,5]
Print(sol.MaxSlidingWindow(nums6, 3)); // [2,2]
}
}
package main
import (
"fmt"
"math"
)
func maxSlidingWindow(nums []int, k int) []int {
n := len(nums)
if n == 0 || k == 0 || k > n {
return []int{}
}
result := make([]int, 0, n-k+1)
for i := 0; i <= n-k; i++ {
maxVal := math.MinInt32
for j := i; j < i+k; j++ {
if nums[j] > maxVal {
maxVal = nums[j]
}
}
result = append(result, maxVal)
}
return result
}
func main() {
nums1 := []int{1, 3, -1, -3, 5, 3, 6, 7}
nums2 := []int{1}
nums3 := []int{1, -1}
nums4 := []int{5, 4, 3, 2, 1}
nums5 := []int{1, 2, 3, 4, 5}
nums6 := []int{2, 2, 2, 2}
nums7 := []int{}
printSlice := func(arr []int) {
fmt.Printf("[")
for i, v := range arr {
fmt.Print(v)
if i < len(arr)-1 {
fmt.Print(",")
}
}
fmt.Println("]")
}
printSlice(maxSlidingWindow(nums1, 3))
printSlice(maxSlidingWindow(nums2, 1))
printSlice(maxSlidingWindow(nums3, 1))
printSlice(maxSlidingWindow(nums7, 0))
printSlice(maxSlidingWindow(nums4, 2))
printSlice(maxSlidingWindow(nums5, 2))
printSlice(maxSlidingWindow(nums6, 3))
}
package main
import (
"fmt"
"math"
)
// maxSlidingWindow computes the maximum value in each sliding window of size k.
// Brute-force approach: O(n * k) time, O(1) extra space (n = len(nums)).
//
// Why this solution:
// - Simplest implementation, requiring no additional data structures beyond the output slice.
// - Ideal for interviews to demonstrate initial problem understanding before optimization.
// - Guarantees correctness by recomputing max for each window.
//
// Where to use:
// - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
// - To validate optimized solutions during development or debugging.
// - When space is critical, though time complexity may cause timeouts for large inputs.
//
// Complexity:
// - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
// For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
// - Space: O(1) - Excluding output slice, only uses a few variables.
//
// Interview notes:
// - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
// - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
// - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
// - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
// - Handles all cases correctly since it recomputes fresh for each window.
// - Go note: Uses math.MinInt32 for smallest int, efficient slice operations.
//
// Edge cases handled:
// - Empty array or k=0: Returns empty slice.
// - k=1: Returns the input array, as each element is its own max.
// - k=n: Returns a single element, the global max.
// - Decreasing sequence: Max is always leftmost in window.
// - Increasing sequence: Max is always rightmost.
// - All equal elements: Returns the same value for all windows.
// - Negatives/zeros: Comparisons handle all values correctly.
//
// ASCII Example for nums=[1,3,-1], k=3:
// Window 0-2: [1,3,-1]
// - Scan: 1, 3 (max so far), -1 < 3 → max = 3
// Output: [3]
//
// ASCII for nums=[5,4,3], k=2:
// Window 0-1: [5,4] → max = 5
// Window 1-2: [4,3] → max = 4
// Output: [5,4]
func maxSlidingWindow(nums []int, k int) []int {
n := len(nums)
if n == 0 || k == 0 || k > n {
return []int{} // Handle invalid/empty cases
}
result := make([]int, 0, n-k+1)
for i := 0; i <= n-k; i++ { // Loop over window starts
maxVal := math.MinInt32 // Initialize to smallest possible
for j := i; j < i+k; j++ { // Scan window
if nums[j] > maxVal {
maxVal = nums[j] // Update max
}
}
result = append(result, maxVal) // Add to result
}
return result
}
func main() {
nums1 := []int{1, 3, -1, -3, 5, 3, 6, 7}
nums2 := []int{1}
nums3 := []int{1, -1}
nums4 := []int{5, 4, 3, 2, 1}
nums5 := []int{1, 2, 3, 4, 5}
nums6 := []int{2, 2, 2, 2}
nums7 := []int{}
printSlice := func(arr []int) {
fmt.Printf("[")
for i, v := range arr {
fmt.Print(v)
if i < len(arr)-1 {
fmt.Print(",")
}
}
fmt.Println("]")
}
printSlice(maxSlidingWindow(nums1, 3)) // [3,3,5,5,6,7]
printSlice(maxSlidingWindow(nums2, 1)) // [1]
printSlice(maxSlidingWindow(nums3, 1)) // [1,-1]
printSlice(maxSlidingWindow(nums7, 0)) // []
printSlice(maxSlidingWindow(nums4, 2)) // [5,4,3,2]
printSlice(maxSlidingWindow(nums5, 2)) // [2,3,4,5]
printSlice(maxSlidingWindow(nums6, 3)) // [2,2]
}
package main
import (
"fmt"
"container/list"
)
func maxSlidingWindow(nums []int, k int) []int {
n := len(nums)
if n == 0 || k == 0 || k > n {
return []int{}
}
deq := list.New()
result := make([]int, 0, n-k+1)
for i := 0; i < n; i++ {
if deq.Len() > 0 && deq.Front().Value.(int) == i-k {
deq.Remove(deq.Front())
}
for deq.Len() > 0 && nums[deq.Back().Value.(int)] <= nums[i] {
deq.Remove(deq.Back())
}
deq.PushBack(i)
if i >= k-1 {
result = append(result, nums[deq.Front().Value.(int)])
}
}
return result
}
func main() {
nums1 := []int{1, 3, -1, -3, 5, 3, 6, 7}
nums2 := []int{1}
nums3 := []int{1, -1}
nums4 := []int{5, 4, 3, 2, 1}
nums5 := []int{1, 2, 3, 4, 5}
nums6 := []int{2, 2, 2, 2}
nums7 := []int{}
printSlice := func(arr []int) {
fmt.Printf("[")
for i, v := range arr {
fmt.Print(v)
if i < len(arr)-1 {
fmt.Print(",")
}
}
fmt.Println("]")
}
printSlice(maxSlidingWindow(nums1, 3))
printSlice(maxSlidingWindow(nums2, 1))
printSlice(maxSlidingWindow(nums3, 1))
printSlice(maxSlidingWindow(nums7, 0))
printSlice(maxSlidingWindow(nums4, 2))
printSlice(maxSlidingWindow(nums5, 2))
printSlice(maxSlidingWindow(nums6, 3))
}
package main
import (
"fmt"
"container/list"
)
// maxSlidingWindow computes the maximum value in each sliding window of size k.
// Optimized approach using monotonic deque: O(n) time, O(k) space.
//
// Why this solution:
// - Eliminates redundant max computations by maintaining only potential max candidates.
// - Deque ensures front is the max, and back is kept in decreasing order.
// - Outperforms max-heap (O(n log k)) for large inputs.
//
// Where to use:
// - Large inputs (n,k up to 10^5) where O(nk) would timeout.
// - Streaming data scenarios requiring real-time max in fixed windows.
// - When O(n) time is critical for performance.
//
// Complexity:
// - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
// - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
//
// Interview notes:
// - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
// - Why indices: "To check if front index is in window [i-k+1, i]."
// - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
// - Compare to brute: "O(nk) to O(n), critical for n=10^5."
// - Go note: Uses container/list for O(1) front/back operations, unlike slice remove-first O(n).
// - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
// - Amortized analysis: "Each index in/out once, total 2n operations."
//
// Edge cases handled:
// - Empty array or k=0: Return empty slice.
// - k=1: Each element is max, deque size 1 per iteration.
// - k=n: Single window, deque builds to global max.
// - Decreasing sequence: Deque grows to k, front always largest.
// - Increasing sequence: Frequent pops, deque often size 1 (latest max).
// - Duplicates: Pop if >= to keep leftmost index for correct window timing.
// - Negatives/zeros: Comparisons handle all values correctly.
// - All equal elements: Pop >= keeps deque size 1, slides correctly.
//
// ASCII Example for nums=[1,3,-1,-3,5], k=3:
// i=0: deq=[] → push 0 → deq=[0] (1)
// i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
// i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
// i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
// i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
func maxSlidingWindow(nums []int, k int) []int {
n := len(nums)
if n == 0 || k == 0 || k > n {
return []int{}
}
deq := list.New() // Deque of indices
result := make([]int, 0, n-k+1)
for i := 0; i < n; i++ {
// Remove out-of-window front
if deq.Len() > 0 && deq.Front().Value.(int) == i-k {
deq.Remove(deq.Front())
}
// Remove smaller or equal from back
for deq.Len() > 0 && nums[deq.Back().Value.(int)] <= nums[i] {
deq.Remove(deq.Back())
}
// Add current index
deq.PushBack(i)
// Add max to result if window is full
if i >= k-1 {
result = append(result, nums[deq.Front().Value.(int)])
}
}
return result
}
func main() {
nums1 := []int{1, 3, -1, -3, 5, 3, 6, 7}
nums2 := []int{1}
nums3 := []int{1, -1}
nums4 := []int{5, 4, 3, 2, 1}
nums5 := []int{1, 2, 3, 4, 5}
nums6 := []int{2, 2, 2, 2}
nums7 := []int{}
printSlice := func(arr []int) {
fmt.Printf("[")
for i, v := range arr {
fmt.Print(v)
if i < len(arr)-1 {
fmt.Print(",")
}
}
fmt.Println("]")
}
printSlice(maxSlidingWindow(nums1, 3)) // [3,3,5,5,6,7]
printSlice(maxSlidingWindow(nums2, 1)) // [1]
printSlice(maxSlidingWindow(nums3, 1)) // [1,-1]
printSlice(maxSlidingWindow(nums7, 0)) // []
printSlice(maxSlidingWindow(nums4, 2)) // [5,4,3,2]
printSlice(maxSlidingWindow(nums5, 2)) // [2,3,4,5]
printSlice(maxSlidingWindow(nums6, 3)) // [2,2]
}
class BruteSolution1 {
maxSlidingWindow(nums: number[], k: number): number[] {
const n: number = nums.length;
if (n === 0 || k === 0 || k > n) return [];
const result: number[] = [];
for (let i = 0; i <= n - k; i++) {
let maxVal: number = Number.MIN_SAFE_INTEGER;
for (let j = i; j < i + k; j++) {
maxVal = Math.max(maxVal, nums[j]);
}
result.push(maxVal);
}
return result;
}
}
const sol = new BruteSolution1();
const nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
const nums2 = [1];
const nums3 = [1, -1];
const nums4 = [5, 4, 3, 2, 1];
const nums5 = [1, 2, 3, 4, 5];
const nums6 = [2, 2, 2, 2];
const nums7: number[] = [];
function printArray1(arr: number[]): void {
console.log(`[${arr.join(',')}]`);
}
printArray(sol.maxSlidingWindow(nums1, 3));
printArray(sol.maxSlidingWindow(nums2, 1));
printArray(sol.maxSlidingWindow(nums3, 1));
printArray(sol.maxSlidingWindow(nums7, 0));
printArray(sol.maxSlidingWindow(nums4, 2));
printArray(sol.maxSlidingWindow(nums5, 2));
printArray(sol.maxSlidingWindow(nums6, 3));
/**
* File: typescript.brute.ts
* Brute-force Sliding Window Maximum
* O(n * k) time, O(1) extra space (n = nums.length)
*
* Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
* This approach directly computes the max for each window by scanning, ensuring correctness but suffering
* from high time complexity due to redundant computations across overlapping windows.
*
* Why this solution:
* - Simplest implementation, requiring no additional data structures beyond the output array.
* - Ideal for interviews to demonstrate initial problem understanding before optimization.
* - Guarantees correctness for all inputs by recomputing max for each window.
*
* Where to use:
* - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
* - To validate optimized solutions during development or debugging.
* - When space is critical, though time complexity may cause timeouts for large inputs.
*
* Complexity:
* - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
* For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
* - Space: O(1) - Excluding output array, only uses a few variables.
*
* Interview notes:
* - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
* - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
* - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
* - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
* - Handles all cases correctly since it recomputes fresh for each window.
* - TypeScript note: Uses Number.MIN_SAFE_INTEGER for smallest number, type safety for arrays.
*
* Edge cases handled:
* - Empty array or k=0: Returns empty array.
* - k=1: Returns the input array, as each element is its own max.
* - k=n: Returns a single element, the global max.
* - Decreasing sequence: Max is always leftmost in window.
* - Increasing sequence: Max is always rightmost.
* - All equal elements: Returns the same value for all windows.
* - Negatives/zeros: Comparisons handle all values correctly.
*
* ASCII Example for nums=[1,3,-1], k=3:
* Window 0-2: [1,3,-1]
* - Scan: 1, 3 (max so far), -1 < 3 → max = 3
* Output: [3]
*
* ASCII for nums=[5,4,3], k=2:
* Window 0-1: [5,4] → max = 5
* Window 1-2: [4,3] → max = 4
* Output: [5,4]
*/
class BruteSolution1 {
maxSlidingWindow(nums: number[], k: number): number[] {
const n: number = nums.length;
if (n === 0 || k === 0 || k > n) return []; // Handle invalid/empty cases
const result: number[] = [];
for (let i = 0; i <= n - k; i++) { // Loop over window starts
let maxVal: number = Number.MIN_SAFE_INTEGER; // Initialize to smallest possible
for (let j = i; j < i + k; j++) { // Scan window
maxVal = Math.max(maxVal, nums[j]); // Update max
}
result.push(maxVal); // Add to result
}
return result;
}
}
// Test runner
const sol = new BruteSolution1();
const nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
const nums2 = [1];
const nums3 = [1, -1];
const nums4 = [5, 4, 3, 2, 1];
const nums5 = [1, 2, 3, 4, 5];
const nums6 = [2, 2, 2, 2];
const nums7: number[] = [];
function printArray1(arr: number[]): void {
console.log(`[${arr.join(',')}]`);
}
printArray(sol.maxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
printArray(sol.maxSlidingWindow(nums2, 1)); // [1]
printArray(sol.maxSlidingWindow(nums3, 1)); // [1,-1]
printArray(sol.maxSlidingWindow(nums7, 0)); // []
printArray(sol.maxSlidingWindow(nums4, 2)); // [5,4,3,2]
printArray(sol.maxSlidingWindow(nums5, 2)); // [2,3,4,5]
printArray(sol.maxSlidingWindow(nums6, 3)); // [2,2]
class OptimizedSolution {
maxSlidingWindow(nums: number[], k: number): number[] {
const n: number = nums.length;
if (n === 0 || k === 0 || k > n) return [];
const deq: number[] = [];
const result: number[] = [];
for (let i = 0; i < n; i++) {
if (deq.length > 0 && deq[0] === i - k) {
deq.shift();
}
while (deq.length > 0 && nums[deq[deq.length - 1]] <= nums[i]) {
deq.pop();
}
deq.push(i);
if (i >= k - 1) {
result.push(nums[deq[0]]);
}
}
return result;
}
}
const sol = new OptimizedSolution();
const nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
const nums2 = [1];
const nums3 = [1, -1];
const nums4 = [5, 4, 3, 2, 1];
const nums5 = [1, 2, 3, 4, 5];
const nums6 = [2, 2, 2, 2];
const nums7: number[] = [];
function printArray(arr: number[]): void {
console.log(`[${arr.join(',')}]`);
}
printArray(sol.maxSlidingWindow(nums1, 3));
printArray(sol.maxSlidingWindow(nums2, 1));
printArray(sol.maxSlidingWindow(nums3, 1));
printArray(sol.maxSlidingWindow(nums7, 0));
printArray(sol.maxSlidingWindow(nums4, 2));
printArray(sol.maxSlidingWindow(nums5, 2));
printArray(sol.maxSlidingWindow(nums6, 3));
/**
* File: typescript.optimized.ts
* Optimized Sliding Window Maximum using Monotonic Deque
* O(n) time, O(k) space
*
* Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
* of the max in the current window. For each index i:
* - Pop front if index is out of current window (i - k).
* - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
* - Push i to back.
* - If i >= k-1, add nums[front] to result (current window max).
* This ensures O(n) time as each element is pushed/popped at most once.
*
* Why this solution:
* - Eliminates redundant max computations by maintaining only potential max candidates.
* - Deque ensures front is the max, and back is kept in decreasing order.
* - Outperforms max-heap (O(n log k)) for large inputs.
*
* Where to use:
* - Large inputs (n,k up to 10^5) where O(nk) would timeout.
* - Streaming data scenarios requiring real-time max in fixed windows.
* - When O(n) time is critical for performance.
*
* Complexity:
* - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
* - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
*
* Interview notes:
* - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
* - Why indices: "To check if front index is in window [i-k+1, i]."
* - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
* - Compare to brute: "O(nk) to O(n), critical for n=10^5."
* - TypeScript note: Uses array as deque; push/pop O(1), shift O(n), but total operations O(n).
* - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
* - Amortized analysis: "Each index in/out once, total 2n operations."
*
* Edge cases handled:
* - Empty array or k=0: Return empty array.
* - k=1: Each element is max, deque size 1 per iteration.
* - k=n: Single window, deque builds to global max.
* - Decreasing sequence: Deque grows to k, front always largest.
* - Increasing sequence: Frequent pops, deque often size 1 (latest max).
* - Duplicates: Pop if >= to keep leftmost index for correct window timing.
* - Negatives/zeros: Comparisons handle all values correctly.
* - All equal elements: Pop >= keeps deque size 1, slides correctly.
*
* ASCII Example for nums=[1,3,-1,-3,5], k=3:
* i=0: deq=[] → push 0 → deq=[0] (1)
* i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
* i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
* i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
* i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
*/
class OptimizedSolution {
maxSlidingWindow(nums: number[], k: number): number[] {
const n: number = nums.length;
if (n === 0 || k === 0 || k > n) return [];
const deq: number[] = []; // Array as deque
const result: number[] = [];
for (let i = 0; i < n; i++) {
// Remove out-of-window front
if (deq.length > 0 && deq[0] === i - k) {
deq.shift();
}
// Remove smaller or equal from back
while (deq.length > 0 && nums[deq[deq.length - 1]] <= nums[i]) {
deq.pop();
}
// Add current index
deq.push(i);
// Add max to result if window is full
if (i >= k - 1) {
result.push(nums[deq[0]]);
}
}
return result;
}
}
// Test runner
const sol = new OptimizedSolution();
const nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
const nums2 = [1];
const nums3 = [1, -1];
const nums4 = [5, 4, 3, 2, 1];
const nums5 = [1, 2, 3, 4, 5];
const nums6 = [2, 2, 2, 2];
const nums7: number[] = [];
function printArray(arr: number[]): void {
console.log(`[${arr.join(',')}]`);
}
printArray(sol.maxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
printArray(sol.maxSlidingWindow(nums2, 1)); // [1]
printArray(sol.maxSlidingWindow(nums3, 1)); // [1,-1]
printArray(sol.maxSlidingWindow(nums7, 0)); // []
printArray(sol.maxSlidingWindow(nums4, 2)); // [5,4,3,2]
printArray(sol.maxSlidingWindow(nums5, 2)); // [2,3,4,5]
printArray(sol.maxSlidingWindow(nums6, 3)); // [2,2]
class Solution
def max_sliding_window(nums, k)
n = nums.length
return [] if n == 0 || k == 0 || k > n
result = []
(0..n-k).each do |i|
max_val = -Float::INFINITY
(i...i+k).each do |j|
max_val = [max_val, nums[j]].max
end
result << max_val
end
result
end
end
sol = Solution.new
nums1 = [1, 3, -1, -3, 5, 3, 6, 7]
nums2 = [1]
nums3 = [1, -1]
nums4 = [5, 4, 3, 2, 1]
nums5 = [1, 2, 3, 4, 5]
nums6 = [2, 2, 2, 2]
nums7 = []
def print_array(arr)
puts "[#{arr.join(',')}]"
end
print_array(sol.max_sliding_window(nums1, 3))
print_array(sol.max_sliding_window(nums2, 1))
print_array(sol.max_sliding_window(nums3, 1))
print_array(sol.max_sliding_window(nums7, 0))
print_array(sol.max_sliding_window(nums4, 2))
print_array(sol.max_sliding_window(nums5, 2))
print_array(sol.max_sliding_window(nums6, 3))
# File: ruby.brute.rb
# Brute-force Sliding Window Maximum
# O(n * k) time, O(1) extra space (n = nums.length)
#
# Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
# This approach directly computes the max for each window by scanning, ensuring correctness but suffering
# from high time complexity due to redundant computations across overlapping windows.
#
# Why this solution:
# - Simplest implementation, requiring no additional data structures beyond the output array.
# - Ideal for interviews to demonstrate initial problem understanding before optimization.
# - Guarantees correctness for all inputs by recomputing max for each window.
#
# Where to use:
# - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
# - To validate optimized solutions during development or debugging.
# - When space is critical, though time complexity may cause timeouts for large inputs.
#
# Complexity:
# - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
# For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
# - Space: O(1) - Excluding output array, only uses a few variables.
#
# Interview notes:
# - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
# - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
# - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
# - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
# - Handles all cases correctly since it recomputes fresh for each window.
# - Ruby note: Uses -Float::INFINITY for smallest number, array operations are idiomatic.
#
# Edge cases handled:
# - Empty array or k=0: Returns empty array.
# - k=1: Returns the input array, as each element is its own max.
# - k=n: Returns a single element, the global max.
# - Decreasing sequence: Max is always leftmost in window.
# - Increasing sequence: Max is always rightmost.
# - All equal elements: Returns the same value for all windows.
# - Negatives/zeros: Comparisons handle all values correctly.
#
# ASCII Example for nums=[1,3,-1], k=3:
# Window 0-2: [1,3,-1]
# - Scan: 1, 3 (max so far), -1 < 3 → max = 3
# Output: [3]
#
# ASCII for nums=[5,4,3], k=2:
# Window 0-1: [5,4] → max = 5
# Window 1-2: [4,3] → max = 4
# Output: [5,4]
#
class Solution
# @param {Integer[]} nums
# @param {Integer} k
# @return {Integer[]}
def max_sliding_window(nums, k)
n = nums.length
return [] if n == 0 || k == 0 || k > n # Handle invalid/empty cases
result = []
(0..n-k).each do |i| # Loop over window starts
max_val = -Float::INFINITY # Initialize to smallest possible
(i...i+k).each do |j| # Scan window
max_val = [max_val, nums[j]].max # Update max
end
result << max_val # Add to result
end
result
end
end
# Test runner
sol = Solution.new
nums1 = [1, 3, -1, -3, 5, 3, 6, 7]
nums2 = [1]
nums3 = [1, -1]
nums4 = [5, 4, 3, 2, 1]
nums5 = [1, 2, 3, 4, 5]
nums6 = [2, 2, 2, 2]
nums7 = []
def print_array(arr)
puts "[#{arr.join(',')}]"
end
print_array(sol.max_sliding_window(nums1, 3)) # [3,3,5,5,6,7]
print_array(sol.max_sliding_window(nums2, 1)) # [1]
print_array(sol.max_sliding_window(nums3, 1)) # [1,-1]
print_array(sol.max_sliding_window(nums7, 0)) # []
print_array(sol.max_sliding_window(nums4, 2)) # [5,4,3,2]
print_array(sol.max_sliding_window(nums5, 2)) # [2,3,4,5]
print_array(sol.max_sliding_window(nums6, 3)) # [2,2]
class Solution
def max_sliding_window(nums, k)
n = nums.length
return [] if n == 0 || k == 0 || k > n
deq = []
result = []
(0...n).each do |i|
deq.shift if !deq.empty? && deq[0] == i - k
deq.pop while !deq.empty? && nums[deq[-1]] <= nums[i]
deq << i
result << nums[deq[0]] if i >= k - 1
end
result
end
end
sol = Solution.new
nums1 = [1, 3, -1, -3, 5, 3, 6, 7]
nums2 = [1]
nums3 = [1, -1]
nums4 = [5, 4, 3, 2, 1]
nums5 = [1, 2, 3, 4, 5]
nums6 = [2, 2, 2, 2]
nums7 = []
def print_array(arr)
puts "[#{arr.join(',')}]"
end
print_array(sol.max_sliding_window(nums1, 3))
print_array(sol.max_sliding_window(nums2, 1))
print_array(sol.max_sliding_window(nums3, 1))
print_array(sol.max_sliding_window(nums7, 0))
print_array(sol.max_sliding_window(nums4, 2))
print_array(sol.max_sliding_window(nums5, 2))
print_array(sol.max_sliding_window(nums6, 3))
# File: ruby.optimized.rb
# Optimized Sliding Window Maximum using Monotonic Deque
# O(n) time, O(k) space
#
# Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
# of the max in the current window. For each index i:
# - Pop front if index is out of current window (i - k).
# - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
# - Push i to back.
# - If i >= k-1, add nums[front] to result (current window max).
# This ensures O(n) time as each element is pushed/popped at most once.
#
# Why this solution:
# - Eliminates redundant max computations by maintaining only potential max candidates.
# - Deque ensures front is the max, and back is kept in decreasing order.
# - Outperforms max-heap (O(n log k)) for large inputs.
#
# Where to use:
# - Large inputs (n,k up to 10^5) where O(nk) would timeout.
# - Streaming data scenarios requiring real-time max in fixed windows.
# - When O(n) time is critical for performance.
#
# Complexity:
# - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
# - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
#
# Interview notes:
# - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
# - Why indices: "To check if front index is in window [i-k+1, i]."
# - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
# - Compare to brute: "O(nk) to O(n), critical for n=10^5."
# - Ruby note: Uses array as deque; pop/push O(1), shift O(n), but total operations O(n).
# - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
# - Amortized analysis: "Each index in/out once, total 2n operations."
#
# Edge cases handled:
# - Empty array or k=0: Return empty array.
# - k=1: Each element is max, deque size 1 per iteration.
# - k=n: Single window, deque builds to global max.
# - Decreasing sequence: Deque grows to k, front always largest.
# - Increasing sequence: Frequent pops, deque often size 1 (latest max).
# - Duplicates: Pop if >= to keep leftmost index for correct window timing.
# - Negatives/zeros: Comparisons handle all values correctly.
# - All equal elements: Pop >= keeps deque size 1, slides correctly.
#
# ASCII Example for nums=[1,3,-1,-3,5], k=3:
# i=0: deq=[] → push 0 → deq=[0] (1)
# i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
# i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
# i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
# i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
#
class Solution
# @param {Integer[]} nums
# @param {Integer} k
# @return {Integer[]}
def max_sliding_window(nums, k)
n = nums.length
return [] if n == 0 || k == 0 || k > n
deq = [] # Array as deque
result = []
(0...n).each do |i|
# Remove out-of-window front
deq.shift if !deq.empty? && deq[0] == i - k
# Remove smaller or equal from back
deq.pop while !deq.empty? && nums[deq[-1]] <= nums[i]
# Add current index
deq << i
# Add max to result if window is full
result << nums[deq[0]] if i >= k - 1
end
result
end
end
# Test runner
sol = Solution.new
nums1 = [1, 3, -1, -3, 5, 3, 6, 7]
nums2 = [1]
nums3 = [1, -1]
nums4 = [5, 4, 3, 2, 1]
nums5 = [1, 2, 3, 4, 5]
nums6 = [2, 2, 2, 2]
nums7 = []
def print_array(arr)
puts "[#{arr.join(',')}]"
end
print_array(sol.max_sliding_window(nums1, 3)) # [3,3,5,5,6,7]
print_array(sol.max_sliding_window(nums2, 1)) # [1]
print_array(sol.max_sliding_window(nums3, 1)) # [1,-1]
print_array(sol.max_sliding_window(nums7, 0)) # []
print_array(sol.max_sliding_window(nums4, 2)) # [5,4,3,2]
print_array(sol.max_sliding_window(nums5, 2)) # [2,3,4,5]
print_array(sol.max_sliding_window(nums6, 3)) # [2,2]
class Solution {
func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] {
let n = nums.count
if n == 0 || k == 0 || k > n { return [] }
var result: [Int] = []
for i in 0...(n - k) {
var maxVal = Int.min
for j in i..<(i + k) {
maxVal = max(maxVal, nums[j])
}
result.append(maxVal)
}
return result
}
}
let sol = Solution()
let nums1 = [1, 3, -1, -3, 5, 3, 6, 7]
let nums2 = [1]
let nums3 = [1, -1]
let nums4 = [5, 4, 3, 2, 1]
let nums5 = [1, 2, 3, 4, 5]
let nums6 = [2, 2, 2, 2]
let nums7: [Int] = []
func printArray(_ arr: [Int]) {
print("[" + arr.map { String($0) }.joined(separator: ",") + "]")
}
printArray(sol.maxSlidingWindow(nums1, 3))
printArray(sol.maxSlidingWindow(nums2, 1))
printArray(sol.maxSlidingWindow(nums3, 1))
printArray(sol.maxSlidingWindow(nums7, 0))
printArray(sol.maxSlidingWindow(nums4, 2))
printArray(sol.maxSlidingWindow(nums5, 2))
printArray(sol.maxSlidingWindow(nums6, 3))
// File: swift.brute.swift
// Brute-force Sliding Window Maximum
// O(n * k) time, O(1) extra space (n = nums.count)
/// Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
/// This approach directly computes the max for each window by scanning, ensuring correctness but suffering
/// from high time complexity due to redundant computations across overlapping windows.
///
/// Why this solution:
/// - Simplest implementation, requiring no additional data structures beyond the output array.
/// - Ideal for interviews to demonstrate initial problem understanding before optimization.
/// - Guarantees correctness for all inputs by recomputing max for each window.
///
/// Where to use:
/// - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
/// - To validate optimized solutions during development or debugging.
/// - When space is critical, though time complexity may cause timeouts for large inputs.
///
/// Complexity:
/// - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
/// For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
/// - Space: O(1) - Excluding output array, only uses a few variables.
///
/// Interview notes:
/// - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
/// - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
/// - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
/// - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
/// - Handles all cases correctly since it recomputes fresh for each window.
/// - Swift note: Uses Int.min for smallest integer, leverages array safety and concise syntax.
///
/// Edge cases handled:
/// - Empty array or k=0: Returns empty array.
/// - k=1: Returns the input array, as each element is its own max.
/// - k=n: Returns a single element, the global max.
/// - Decreasing sequence: Max is always leftmost in window.
/// - Increasing sequence: Max is always rightmost.
/// - All equal elements: Returns the same value for all windows.
/// - Negatives/zeros: Comparisons handle all values correctly.
///
/// ASCII Example for nums=[1,3,-1], k=3:
/// Window 0-2: [1,3,-1]
/// - Scan: 1, 3 (max so far), -1 < 3 → max = 3
/// Output: [3]
///
/// ASCII for nums=[5,4,3], k=2:
/// Window 0-1: [5,4] → max = 5
/// Window 1-2: [4,3] → max = 4
/// Output: [5,4]
///
class Solution {
func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] {
let n = nums.count
if n == 0 || k == 0 || k > n { return [] } // Handle invalid/empty cases
var result: [Int] = []
for i in 0...(n - k) { // Loop over window starts
var maxVal = Int.min // Initialize to smallest possible
for j in i..<(i + k) { // Scan window
maxVal = max(maxVal, nums[j]) // Update max
}
result.append(maxVal) // Add to result
}
return result
}
}
// Test runner
let sol = Solution()
let nums1 = [1, 3, -1, -3, 5, 3, 6, 7]
let nums2 = [1]
let nums3 = [1, -1]
let nums4 = [5, 4, 3, 2, 1]
let nums5 = [1, 2, 3, 4, 5]
let nums6 = [2, 2, 2, 2]
let nums7: [Int] = []
func printArray(_ arr: [Int]) {
print("[" + arr.map { String($0) }.joined(separator: ",") + "]")
}
printArray(sol.maxSlidingWindow(nums1, 3)) // [3,3,5,5,6,7]
printArray(sol.maxSlidingWindow(nums2, 1)) // [1]
printArray(sol.maxSlidingWindow(nums3, 1)) // [1,-1]
printArray(sol.maxSlidingWindow(nums7, 0)) // []
printArray(sol.maxSlidingWindow(nums4, 2)) // [5,4,3,2]
printArray(sol.maxSlidingWindow(nums5, 2)) // [2,3,4,5]
printArray(sol.maxSlidingWindow(nums6, 3)) // [2,2]
class Solution {
func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] {
let n = nums.count
if n == 0 || k == 0 || k > n { return [] }
var deq: [Int] = []
var result: [Int] = []
for i in 0..<n {
if !deq.isEmpty && deq[0] == i - k {
deq.removeFirst()
}
while !deq.isEmpty && nums[deq.last!] <= nums[i] {
deq.removeLast()
}
deq.append(i)
if i >= k - 1 {
result.append(nums[deq[0]])
}
}
return result
}
}
let sol = Solution()
let nums1 = [1, 3, -1, -3, 5, 3, 6, 7]
let nums2 = [1]
let nums3 = [1, -1]
let nums4 = [5, 4, 3, 2, 1]
let nums5 = [1, 2, 3, 4, 5]
let nums6 = [2, 2, 2, 2]
let nums7: [Int] = []
func printArray(_ arr: [Int]) {
print("[" + arr.map { String($0) }.joined(separator: ",") + "]")
}
printArray(sol.maxSlidingWindow(nums1, 3))
printArray(sol.maxSlidingWindow(nums2, 1))
printArray(sol.maxSlidingWindow(nums3, 1))
printArray(sol.maxSlidingWindow(nums7, 0))
printArray(sol.maxSlidingWindow(nums4, 2))
printArray(sol.maxSlidingWindow(nums5, 2))
printArray(sol.maxSlidingWindow(nums6, 3))
// File: swift.optimized.swift
// Optimized Sliding Window Maximum using Monotonic Deque
// O(n) time, O(k) space
/// Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
/// of the max in the current window. For each index i:
/// - Pop front if index is out of current window (i - k).
/// - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
/// - Push i to back.
/// - If i >= k-1, add nums[front] to result (current window max).
/// This ensures O(n) time as each element is pushed/popped at most once.
///
/// Why this solution:
/// - Eliminates redundant max computations by maintaining only potential max candidates.
/// - Deque ensures front is the max, and back is kept in decreasing order.
/// - Outperforms max-heap (O(n log k)) for large inputs.
///
/// Where to use:
/// - Large inputs (n,k up to 10^5) where O(nk) would timeout.
/// - Streaming data scenarios requiring real-time max in fixed windows.
/// - When O(n) time is critical for performance.
///
/// Complexity:
/// - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
/// - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
///
/// Interview notes:
/// - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
/// - Why indices: "To check if front index is in window [i-k+1, i]."
/// - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
/// - Compare to brute: "O(nk) to O(n), critical for n=10^5."
/// - Swift note: Uses array as deque; append/popLast O(1), removeFirst O(n), but total operations O(n).
/// - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
/// - Amortized analysis: "Each index in/out once, total 2n operations."
///
/// Edge cases handled:
/// - Empty array or k=0: Return empty array.
/// - k=1: Each element is max, deque size 1 per iteration.
/// - k=n: Single window, deque builds to global max.
/// - Decreasing sequence: Deque grows to k, front always largest.
/// - Increasing sequence: Frequent pops, deque often size 1 (latest max).
/// - Duplicates: Pop if >= to keep leftmost index for correct window timing.
/// - Negatives/zeros: Comparisons handle all values correctly.
/// - All equal elements: Pop >= keeps deque size 1, slides correctly.
///
/// ASCII Example for nums=[1,3,-1,-3,5], k=3:
/// i=0: deq=[] → push 0 → deq=[0] (1)
/// i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
/// i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
/// i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
/// i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
///
class Solution {
func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] {
let n = nums.count
if n == 0 || k == 0 || k > n { return [] }
var deq: [Int] = [] // Array as deque
var result: [Int] = []
for i in 0..<n {
// Remove out-of-window front
if !deq.isEmpty && deq[0] == i - k {
deq.removeFirst()
}
// Remove smaller or equal from back
while !deq.isEmpty && nums[deq.last!] <= nums[i] {
deq.removeLast()
}
// Add current index
deq.append(i)
// Add max to result if window is full
if i >= k - 1 {
result.append(nums[deq[0]])
}
}
return result
}
}
// Test runner
let sol = Solution()
let nums1 = [1, 3, -1, -3, 5, 3, 6, 7]
let nums2 = [1]
let nums3 = [1, -1]
let nums4 = [5, 4, 3, 2, 1]
let nums5 = [1, 2, 3, 4, 5]
let nums6 = [2, 2, 2, 2]
let nums7: [Int] = []
func printArray(_ arr: [Int]) {
print("[" + arr.map { String($0) }.joined(separator: ",") + "]")
}
printArray(sol.maxSlidingWindow(nums1, 3)) // [3,3,5,5,6,7]
printArray(sol.maxSlidingWindow(nums2, 1)) // [1]
printArray(sol.maxSlidingWindow(nums3, 1)) // [1,-1]
printArray(sol.maxSlidingWindow(nums7, 0)) // []
printArray(sol.maxSlidingWindow(nums4, 2)) // [5,4,3,2]
printArray(sol.maxSlidingWindow(nums5, 2)) // [2,3,4,5]
printArray(sol.maxSlidingWindow(nums6, 3)) // [2,2]
package slidingWindowMaximum
import kotlin.math.max
class Solution {
fun maxSlidingWindow(nums: IntArray, k: Int): IntArray {
val n = nums.size
if (n == 0 || k == 0 || k > n) return intArrayOf()
val result = IntArray(n - k + 1)
for (i in 0..n - k) {
var maxVal = Int.MIN_VALUE
for (j in i until i + k) {
maxVal = max(maxVal, nums[j])
}
result[i] = maxVal
}
return result
}
}
fun main() {
val sol = Solution()
val nums1 = intArrayOf(1, 3, -1, -3, 5, 3, 6, 7)
val nums2 = intArrayOf(1)
val nums3 = intArrayOf(1, -1)
val nums4 = intArrayOf(5, 4, 3, 2, 1)
val nums5 = intArrayOf(1, 2, 3, 4, 5)
val nums6 = intArrayOf(2, 2, 2, 2)
val nums7 = intArrayOf()
fun printArray(arr: IntArray) {
print("[")
arr.forEachIndexed { index, value ->
print(value)
if (index < arr.size - 1) print(",")
}
println("]")
}
printArray(sol.maxSlidingWindow(nums1, 3))
printArray(sol.maxSlidingWindow(nums2, 1))
printArray(sol.maxSlidingWindow(nums3, 1))
printArray(sol.maxSlidingWindow(nums7, 0))
printArray(sol.maxSlidingWindow(nums4, 2))
printArray(sol.maxSlidingWindow(nums5, 2))
printArray(sol.maxSlidingWindow(nums6, 3))
}
package slidingWindowMaximum
import kotlin.math.max
/**
* Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
* This approach directly computes the max for each window by scanning, ensuring correctness but suffering
* from high time complexity due to redundant computations across overlapping windows.
*
* Why this solution:
* - Simplest implementation, requiring no additional data structures beyond the output array.
* - Ideal for interviews to demonstrate initial problem understanding before optimization.
* - Guarantees correctness for all inputs by recomputing max for each window.
*
* Where to use:
* - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
* - To validate optimized solutions during development or debugging.
* - When space is critical, though time complexity may cause timeouts for large inputs.
*
* Complexity:
* - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
* For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
* - Space: O(1) - Excluding output array, only uses a few variables.
*
* Interview notes:
* - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
* - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
* - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
* - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
* - Handles all cases correctly since it recomputes fresh for each window.
* - Kotlin note: Uses Int.MIN_VALUE for smallest int, efficient array operations.
*
* Edge cases handled:
* - Empty array or k=0: Returns empty array.
* - k=1: Returns the input array, as each element is its own max.
* - k=n: Returns a single element, the global max.
* - Decreasing sequence: Max is always leftmost in window.
* - Increasing sequence: Max is always rightmost.
* - All equal elements: Returns the same value for all windows.
* - Negatives/zeros: Comparisons handle all values correctly.
*
* ASCII Example for nums=[1,3,-1], k=3:
* Window 0-2: [1,3,-1]
* - Scan: 1, 3 (max so far), -1 < 3 → max = 3
* Output: [3]
*
* ASCII for nums=[5,4,3], k=2:
* Window 0-1: [5,4] → max = 5
* Window 1-2: [4,3] → max = 4
* Output: [5,4]
*
* @param nums Input array of integers
* @param k Sliding window size
* @return Array of maximums for each window
*/
class Solution {
fun maxSlidingWindow(nums: IntArray, k: Int): IntArray {
val n = nums.size
if (n == 0 || k == 0 || k > n) return intArrayOf()
val result = IntArray(n - k + 1)
for (i in 0..n - k) { // Loop over window starts
var maxVal = Int.MIN_VALUE // Initialize to smallest possible
for (j in i until i + k) { // Scan window
maxVal = max(maxVal, nums[j]) // Update max
}
result[i] = maxVal // Add to result
}
return result
}
}
fun main() {
val sol = Solution()
val nums1 = intArrayOf(1, 3, -1, -3, 5, 3, 6, 7)
val nums2 = intArrayOf(1)
val nums3 = intArrayOf(1, -1)
val nums4 = intArrayOf(5, 4, 3, 2, 1)
val nums5 = intArrayOf(1, 2, 3, 4, 5)
val nums6 = intArrayOf(2, 2, 2, 2)
val nums7 = intArrayOf()
fun printArray(arr: IntArray) {
print("[")
arr.forEachIndexed { index, value ->
print(value)
if (index < arr.size - 1) print(",")
}
println("]")
}
printArray(sol.maxSlidingWindow(nums1, 3)) // [3,3,5,5,6,7]
printArray(sol.maxSlidingWindow(nums2, 1)) // [1]
printArray(sol.maxSlidingWindow(nums3, 1)) // [1,-1]
printArray(sol.maxSlidingWindow(nums7, 0)) // []
printArray(sol.maxSlidingWindow(nums4, 2)) // [5,4,3,2]
printArray(sol.maxSlidingWindow(nums5, 2)) // [2,3,4,5]
printArray(sol.maxSlidingWindow(nums6, 3)) // [2,2]
}
package slidingWindowMaximum
import java.util.ArrayDeque
class Solution {
fun maxSlidingWindow(nums: IntArray, k: Int): IntArray {
val n = nums.size
if (n == 0 || k == 0 || k > n) return intArrayOf()
val deq = ArrayDeque<Int>()
val result = IntArray(n - k + 1)
var index = 0
for (i in 0 until n) {
if (deq.isNotEmpty() && deq.first() == i - k) {
deq.removeFirst()
}
while (deq.isNotEmpty() && nums[deq.last()] <= nums[i]) {
deq.removeLast()
}
deq.addLast(i)
if (i >= k - 1) {
result[index++] = nums[deq.first()]
}
}
return result
}
}
fun main() {
val sol = Solution()
val nums1 = intArrayOf(1, 3, -1, -3, 5, 3, 6, 7)
val nums2 = intArrayOf(1)
val nums3 = intArrayOf(1, -1)
val nums4 = intArrayOf(5, 4, 3, 2, 1)
val nums5 = intArrayOf(1, 2, 3, 4, 5)
val nums6 = intArrayOf(2, 2, 2, 2)
val nums7 = intArrayOf()
fun printArray(arr: IntArray) {
print("[")
arr.forEachIndexed { index, value ->
print(value)
if (index < arr.size - 1) print(",")
}
println("]")
}
printArray(sol.maxSlidingWindow(nums1, 3))
printArray(sol.maxSlidingWindow(nums2, 1))
printArray(sol.maxSlidingWindow(nums3, 1))
printArray(sol.maxSlidingWindow(nums7, 0))
printArray(sol.maxSlidingWindow(nums4, 2))
printArray(sol.maxSlidingWindow(nums5, 2))
printArray(sol.maxSlidingWindow(nums6, 3))
}
package slidingWindowMaximum
import java.util.ArrayDeque
/**
* Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
* of the max in the current window. For each index i:
* - Pop front if index is out of current window (i - k).
* - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
* - Push i to back.
* - If i >= k-1, add nums[front] to result (current window max).
* This ensures O(n) time as each element is pushed/popped at most once.
*
* Why this solution:
* - Eliminates redundant max computations by maintaining only potential max candidates.
* - Deque ensures front is the max, and back is kept in decreasing order.
* - Outperforms max-heap (O(n log k)) for large inputs.
*
* Where to use:
* - Large inputs (n,k up to 10^5) where O(nk) would timeout.
* - Streaming data scenarios requiring real-time max in fixed windows.
* - When O(n) time is critical for performance.
*
* Complexity:
* - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
* - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
*
* Interview notes:
* - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
* - Why indices: "To check if front index is in window [i-k+1, i]."
* - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
* - Compare to brute: "O(nk) to O(n), critical for n=10^5."
* - Kotlin note: Uses ArrayDeque for O(1) front/back operations, similar to Java.
* - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
* - Amortized analysis: "Each index in/out once, total 2n operations."
*
* Edge cases handled:
* - Empty array or k=0: Return empty array.
* - k=1: Each element is max, deque size 1 per iteration.
* - k=n: Single window, deque builds to global max.
* - Decreasing sequence: Deque grows to k, front always largest.
* - Increasing sequence: Frequent pops, deque often size 1 (latest max).
* - Duplicates: Pop if >= to keep leftmost index for correct window timing.
* - Negatives/zeros: Comparisons handle all values correctly.
* - All equal elements: Pop >= keeps deque size 1, slides correctly.
*
* ASCII Example for nums=[1,3,-1,-3,5], k=3:
* i=0: deq=[] → push 0 → deq=[0] (1)
* i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
* i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
* i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
* i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
*
* @param nums Input array of integers
* @param k Sliding window size
* @return Array of maximums for each window
*/
class Solution {
fun maxSlidingWindow(nums: IntArray, k: Int): IntArray {
val n = nums.size
if (n == 0 || k == 0 || k > n) return intArrayOf()
val deq = ArrayDeque<Int>() // Deque of indices
val result = IntArray(n - k + 1)
var index = 0
for (i in 0 until n) {
// Remove out-of-window front
if (deq.isNotEmpty() && deq.first() == i - k) {
deq.removeFirst()
}
// Remove smaller or equal from back
while (deq.isNotEmpty() && nums[deq.last()] <= nums[i]) {
deq.removeLast()
}
// Add current index
deq.addLast(i)
// Add max to result if window is full
if (i >= k - 1) {
result[index++] = nums[deq.first()]
}
}
return result
}
}
fun main() {
val sol = Solution()
val nums1 = intArrayOf(1, 3, -1, -3, 5, 3, 6, 7)
val nums2 = intArrayOf(1)
val nums3 = intArrayOf(1, -1)
val nums4 = intArrayOf(5, 4, 3, 2, 1)
val nums5 = intArrayOf(1, 2, 3, 4, 5)
val nums6 = intArrayOf(2, 2, 2, 2)
val nums7 = intArrayOf()
fun printArray(arr: IntArray) {
print("[")
arr.forEachIndexed { index, value ->
print(value)
if (index < arr.size - 1) print(",")
}
println("]")
}
printArray(sol.maxSlidingWindow(nums1, 3)) // [3,3,5,5,6,7]
printArray(sol.maxSlidingWindow(nums2, 1)) // [1]
printArray(sol.maxSlidingWindow(nums3, 1)) // [1,-1]
printArray(sol.maxSlidingWindow(nums7, 0)) // []
printArray(sol.maxSlidingWindow(nums4, 2)) // [5,4,3,2]
printArray(sol.maxSlidingWindow(nums5, 2)) // [2,3,4,5]
printArray(sol.maxSlidingWindow(nums6, 3)) // [2,2]
}
object Solution {
def maxSlidingWindow(nums: Array[Int], k: Int): Array[Int] = {
val n = nums.length
if (n == 0 || k == 0 || k > n) return Array()
val result = new Array[Int](n - k + 1)
for (i <- 0 to n - k) {
var maxVal = Int.MinValue
for (j <- i until i + k) {
maxVal = math.max(maxVal, nums(j))
}
result(i) = maxVal
}
result
}
}
object Main extends App {
val nums1 = Array(1, 3, -1, -3, 5, 3, 6, 7)
val nums2 = Array(1)
val nums3 = Array(1, -1)
val nums4 = Array(5, 4, 3, 2, 1)
val nums5 = Array(1, 2, 3, 4, 5)
val nums6 = Array(2, 2, 2, 2)
val nums7 = Array[Int]()
def printArray(arr: Array[Int]): Unit = {
print("[")
arr.zipWithIndex.foreach { case (v, i) =>
print(v)
if (i < arr.length - 1) print(",")
}
println("]")
}
printArray(Solution.maxSlidingWindow(nums1, 3))
printArray(Solution.maxSlidingWindow(nums2, 1))
printArray(Solution.maxSlidingWindow(nums3, 1))
printArray(Solution.maxSlidingWindow(nums7, 0))
printArray(Solution.maxSlidingWindow(nums4, 2))
printArray(Solution.maxSlidingWindow(nums5, 2))
printArray(Solution.maxSlidingWindow(nums6, 3))
}
// File: scala.brute.scala
// Brute-force Sliding Window Maximum
// O(n * k) time, O(1) extra space (n = nums.length)
/**
* Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
* This approach directly computes the max for each window by scanning, ensuring correctness but suffering
* from high time complexity due to redundant computations across overlapping windows.
*
* Why this solution:
* - Simplest implementation, requiring no additional data structures beyond the output array.
* - Ideal for interviews to demonstrate initial problem understanding before optimization.
* - Guarantees correctness for all inputs by recomputing max for each window.
*
* Where to use:
* - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
* - To validate optimized solutions during development or debugging.
* - When space is critical, though time complexity may cause timeouts for large inputs.
*
* Complexity:
* - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
* For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
* - Space: O(1) - Excluding output array, only uses a few variables.
*
* Interview notes:
* - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
* - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
* - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
* - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
* - Handles all cases correctly since it recomputes fresh for each window.
* - Scala note: Uses Int.MinValue for smallest int, leverages functional-style iteration.
*
* Edge cases handled:
* - Empty array or k=0: Returns empty array.
* - k=1: Returns the input array, as each element is its own max.
* - k=n: Returns a single element, the global max.
* - Decreasing sequence: Max is always leftmost in window.
* - Increasing sequence: Max is always rightmost.
* - All equal elements: Returns the same value for all windows.
* - Negatives/zeros: Comparisons handle all values correctly.
*
* ASCII Example for nums=[1,3,-1], k=3:
* Window 0-2: [1,3,-1]
* - Scan: 1, 3 (max so far), -1 < 3 → max = 3
* Output: [3]
*
* ASCII for nums=[5,4,3], k=2:
* Window 0-1: [5,4] → max = 5
* Window 1-2: [4,3] → max = 4
* Output: [5,4]
*/
object Solution {
def maxSlidingWindow(nums: Array[Int], k: Int): Array[Int] = {
val n = nums.length
if (n == 0 || k == 0 || k > n) return Array()
val result = new Array[Int](n - k + 1)
for (i <- 0 to n - k) { // Loop over window starts
var maxVal = Int.MinValue // Initialize to smallest possible
for (j <- i until i + k) { // Scan window
maxVal = math.max(maxVal, nums(j)) // Update max
}
result(i) = maxVal // Add to result
}
result
}
}
object Main extends App {
val nums1 = Array(1, 3, -1, -3, 5, 3, 6, 7)
val nums2 = Array(1)
val nums3 = Array(1, -1)
val nums4 = Array(5, 4, 3, 2, 1)
val nums5 = Array(1, 2, 3, 4, 5)
val nums6 = Array(2, 2, 2, 2)
val nums7 = Array[Int]()
def printArray(arr: Array[Int]): Unit = {
print("[")
arr.zipWithIndex.foreach { case (v, i) =>
print(v)
if (i < arr.length - 1) print(",")
}
println("]")
}
printArray(Solution.maxSlidingWindow(nums1, 3)) // [3,3,5,5,6,7]
printArray(Solution.maxSlidingWindow(nums2, 1)) // [1]
printArray(Solution.maxSlidingWindow(nums3, 1)) // [1,-1]
printArray(Solution.maxSlidingWindow(nums7, 0)) // []
printArray(Solution.maxSlidingWindow(nums4, 2)) // [5,4,3,2]
printArray(Solution.maxSlidingWindow(nums5, 2)) // [2,3,4,5]
printArray(Solution.maxSlidingWindow(nums6, 3)) // [2,2]
}
import scala.collection.mutable.ArrayDeque
object Solution {
def maxSlidingWindow(nums: Array[Int], k: Int): Array[Int] = {
val n = nums.length
if (n == 0 || k == 0 || k > n) return Array()
val deq = new ArrayDeque[Int]()
val result = new Array[Int](n - k + 1)
var index = 0
for (i <- 0 until n) {
if (deq.nonEmpty && deq.head == i - k) {
deq.removeHead()
}
while (deq.nonEmpty && nums(deq.last) <= nums(i)) {
deq.removeLast()
}
deq.append(i)
if (i >= k - 1) {
result(index) = nums(deq.head)
index += 1
}
}
result
}
}
object Main extends App {
val nums1 = Array(1, 3, -1, -3, 5, 3, 6, 7)
val nums2 = Array(1)
val nums3 = Array(1, -1)
val nums4 = Array(5, 4, 3, 2, 1)
val nums5 = Array(1, 2, 3, 4, 5)
val nums6 = Array(2, 2, 2, 2)
val nums7 = Array[Int]()
def printArray(arr: Array[Int]): Unit = {
print("[")
arr.zipWithIndex.foreach { case (v, i) =>
print(v)
if (i < arr.length - 1) print(",")
}
println("]")
}
printArray(Solution.maxSlidingWindow(nums1, 3))
printArray(Solution.maxSlidingWindow(nums2, 1))
printArray(Solution.maxSlidingWindow(nums3, 1))
printArray(Solution.maxSlidingWindow(nums7, 0))
printArray(Solution.maxSlidingWindow(nums4, 2))
printArray(Solution.maxSlidingWindow(nums5, 2))
printArray(Solution.maxSlidingWindow(nums6, 3))
}
// File: scala.optimized.scala
// Optimized Sliding Window Maximum using Monotonic Deque
// O(n) time, O(k) space
/**
* Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
* of the max in the current window. For each index i:
* - Pop front if index is out of current window (i - k).
* - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
* - Push i to back.
* - If i >= k-1, add nums[front] to result (current window max).
* This ensures O(n) time as each element is pushed/popped at most once.
*
* Why this solution:
* - Eliminates redundant max computations by maintaining only potential max candidates.
* - Deque ensures front is the max, and back is kept in decreasing order.
* - Outperforms max-heap (O(n log k)) for large inputs.
*
* Where to use:
* - Large inputs (n,k up to 10^5) where O(nk) would timeout.
* - Streaming data scenarios requiring real-time max in fixed windows.
* - When O(n) time is critical for performance.
*
* Complexity:
* - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
* - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
*
* Interview notes:
* - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
* - Why indices: "To check if front index is in window [i-k+1, i]."
* - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
* - Compare to brute: "O(nk) to O(n), critical for n=10^5."
* - Scala note: Uses ArrayDeque for O(1) front/back operations, aligning with functional style.
* - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
* - Amortized analysis: "Each index in/out once, total 2n operations."
*
* Edge cases handled:
* - Empty array or k=0: Return empty array.
* - k=1: Each element is max, deque size 1 per iteration.
* - k=n: Single window, deque builds to global max.
* - Decreasing sequence: Deque grows to k, front always largest.
* - Increasing sequence: Frequent pops, deque often size 1 (latest max).
* - Duplicates: Pop if >= to keep leftmost index for correct window timing.
* - Negatives/zeros: Comparisons handle all values correctly.
* - All equal elements: Pop >= keeps deque size 1, slides correctly.
*
* ASCII Example for nums=[1,3,-1,-3,5], k=3:
* i=0: deq=[] → push 0 → deq=[0] (1)
* i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
* i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
* i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
* i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
*/
import scala.collection.mutable.ArrayDeque
object Solution {
def maxSlidingWindow(nums: Array[Int], k: Int): Array[Int] = {
val n = nums.length
if (n == 0 || k == 0 || k > n) return Array()
val deq = new ArrayDeque[Int]()
val result = new Array[Int](n - k + 1)
var index = 0
for (i <- 0 until n) {
// Remove out-of-window front
if (deq.nonEmpty && deq.head == i - k) {
deq.removeHead()
}
// Remove smaller or equal from back
while (deq.nonEmpty && nums(deq.last) <= nums(i)) {
deq.removeLast()
}
// Add current index
deq.append(i)
// Add max to result if window is full
if (i >= k - 1) {
result(index) = nums(deq.head)
index += 1
}
}
result
}
}
object Main extends App {
val nums1 = Array(1, 3, -1, -3, 5, 3, 6, 7)
val nums2 = Array(1)
val nums3 = Array(1, -1)
val nums4 = Array(5, 4, 3, 2, 1)
val nums5 = Array(1, 2, 3, 4, 5)
val nums6 = Array(2, 2, 2, 2)
val nums7 = Array[Int]()
def printArray(arr: Array[Int]): Unit = {
print("[")
arr.zipWithIndex.foreach { case (v, i) =>
print(v)
if (i < arr.length - 1) print(",")
}
println("]")
}
printArray(Solution.maxSlidingWindow(nums1, 3)) // [3,3,5,5,6,7]
printArray(Solution.maxSlidingWindow(nums2, 1)) // [1]
printArray(Solution.maxSlidingWindow(nums3, 1)) // [1,-1]
printArray(Solution.maxSlidingWindow(nums7, 0)) // []
printArray(Solution.maxSlidingWindow(nums4, 2)) // [5,4,3,2]
printArray(Solution.maxSlidingWindow(nums5, 2)) // [2,3,4,5]
printArray(Solution.maxSlidingWindow(nums6, 3)) // [2,2]
}
pub struct Solution;
impl Solution {
pub fn max_sliding_window(nums: Vec<i32>, k: i32) -> Vec<i32> {
let n = nums.len();
let k = k as usize;
if n == 0 || k == 0 || k > n {
return vec![];
}
let mut result = Vec::with_capacity(n - k + 1);
for i in 0..=n - k {
let mut max_val = i32::MIN;
for j in i..i + k {
max_val = max_val.max(nums[j]);
}
result.push(max_val);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
fn print_vec(vec: &[i32]) {
print!("[");
for (i, &val) in vec.iter().enumerate() {
print!("{}", val);
if i < vec.len() - 1 {
print!(",");
}
}
println!("]");
}
#[test]
fn test_max_sliding_window() {
let sol = Solution;
let nums1 = vec![1, 3, -1, -3, 5, 3, 6, 7];
let nums2 = vec![1];
let nums3 = vec![1, -1];
let nums4 = vec![5, 4, 3, 2, 1];
let nums5 = vec![1, 2, 3, 4, 5];
let nums6 = vec![2, 2, 2, 2];
let nums7: Vec<i32> = vec![];
print_vec(&sol.max_sliding_window(nums1, 3));
print_vec(&sol.max_sliding_window(nums2, 1));
print_vec(&sol.max_sliding_window(nums3, 1));
print_vec(&sol.max_sliding_window(nums7, 0));
print_vec(&sol.max_sliding_window(nums4, 2));
print_vec(&sol.max_sliding_window(nums5, 2));
print_vec(&sol.max_sliding_window(nums6, 3));
}
}
fn main() {
let sol = Solution;
let nums1 = vec![1, 3, -1, -3, 5, 3, 6, 7];
let nums2 = vec![1];
let nums3 = vec![1, -1];
let nums4 = vec![5, 4, 3, 2, 1];
let nums5 = vec![1, 2, 3, 4, 5];
let nums6 = vec![2, 2, 2, 2];
let nums7: Vec<i32> = vec![];
fn print_vec(vec: &[i32]) {
print!("[");
for (i, &val) in vec.iter().enumerate() {
print!("{}", val);
if i < vec.len() - 1 {
print!(",");
}
}
println!("]");
}
print_vec(&sol.max_sliding_window(nums1, 3));
print_vec(&sol.max_sliding_window(nums2, 1));
print_vec(&sol.max_sliding_window(nums3, 1));
print_vec(&sol.max_sliding_window(nums7, 0));
print_vec(&sol.max_sliding_window(nums4, 2));
print_vec(&sol.max_sliding_window(nums5, 2));
print_vec(&sol.max_sliding_window(nums6, 3));
}
// File: rust.brute.rs
// Brute-force Sliding Window Maximum
// O(n * k) time, O(1) extra space (n = nums.len())
/// Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
/// This approach directly computes the max for each window by scanning, ensuring correctness but suffering
/// from high time complexity due to redundant computations across overlapping windows.
///
/// Why this solution:
/// - Simplest implementation, requiring no additional data structures beyond the output vector.
/// - Ideal for interviews to demonstrate initial problem understanding before optimization.
/// - Guarantees correctness for all inputs by recomputing max for each window.
///
/// Where to use:
/// - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
/// - To validate optimized solutions during development or debugging.
/// - When space is critical, though time complexity may cause timeouts for large inputs.
///
/// Complexity:
/// - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
/// For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
/// - Space: O(1) - Excluding output vector, only uses a few variables.
///
/// Interview notes:
/// - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
/// - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
/// - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
/// - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
/// - Handles all cases correctly since it recomputes fresh for each window.
/// - Rust note: Uses i32::MIN for smallest int, leverages safe array access and iterator efficiency.
///
/// Edge cases handled:
/// - Empty array or k=0: Returns empty vector.
/// - k=1: Returns the input array, as each element is its own max.
/// - k=n: Returns a single element, the global max.
/// - Decreasing sequence: Max is always leftmost in window.
/// - Increasing sequence: Max is always rightmost.
/// - All equal elements: Returns the same value for all windows.
/// - Negatives/zeros: Comparisons handle all values correctly.
///
/// ASCII Example for nums=[1,3,-1], k=3:
/// Window 0-2: [1,3,-1]
/// - Scan: 1, 3 (max so far), -1 < 3 → max = 3
/// Output: [3]
///
/// ASCII for nums=[5,4,3], k=2:
/// Window 0-1: [5,4] → max = 5
/// Window 1-2: [4,3] → max = 4
/// Output: [5,4]
///
pub struct Solution;
impl Solution {
pub fn max_sliding_window(nums: Vec<i32>, k: i32) -> Vec<i32> {
let n = nums.len();
let k = k as usize;
if n == 0 || k == 0 || k > n {
return vec![];
}
let mut result = Vec::with_capacity(n - k + 1);
for i in 0..=n - k {
let mut max_val = i32::MIN; // Initialize to smallest possible
for j in i..i + k {
max_val = max_val.max(nums[j]); // Update max
}
result.push(max_val); // Add to result
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
fn print_vec(vec: &[i32]) {
print!("[");
for (i, &val) in vec.iter().enumerate() {
print!("{}", val);
if i < vec.len() - 1 {
print!(",");
}
}
println!("]");
}
#[test]
fn test_max_sliding_window() {
let sol = Solution;
let nums1 = vec![1, 3, -1, -3, 5, 3, 6, 7];
let nums2 = vec![1];
let nums3 = vec![1, -1];
let nums4 = vec![5, 4, 3, 2, 1];
let nums5 = vec![1, 2, 3, 4, 5];
let nums6 = vec![2, 2, 2, 2];
let nums7: Vec<i32> = vec![];
print_vec(&sol.max_sliding_window(nums1, 3)); // [3,3,5,5,6,7]
print_vec(&sol.max_sliding_window(nums2, 1)); // [1]
print_vec(&sol.max_sliding_window(nums3, 1)); // [1,-1]
print_vec(&sol.max_sliding_window(nums7, 0)); // []
print_vec(&sol.max_sliding_window(nums4, 2)); // [5,4,3,2]
print_vec(&sol.max_sliding_window(nums5, 2)); // [2,3,4,5]
print_vec(&sol.max_sliding_window(nums6, 3)); // [2,2]
}
}
fn main() {
let sol = Solution;
let nums1 = vec![1, 3, -1, -3, 5, 3, 6, 7];
let nums2 = vec![1];
let nums3 = vec![1, -1];
let nums4 = vec![5, 4, 3, 2, 1];
let nums5 = vec![1, 2, 3, 4, 5];
let nums6 = vec![2, 2, 2, 2];
let nums7: Vec<i32> = vec![];
fn print_vec(vec: &[i32]) {
print!("[");
for (i, &val) in vec.iter().enumerate() {
print!("{}", val);
if i < vec.len() - 1 {
print!(",");
}
}
println!("]");
}
print_vec(&sol.max_sliding_window(nums1, 3)); // [3,3,5,5,6,7]
print_vec(&sol.max_sliding_window(nums2, 1)); // [1]
print_vec(&sol.max_sliding_window(nums3, 1)); // [1,-1]
print_vec(&sol.max_sliding_window(nums7, 0)); // []
print_vec(&sol.max_sliding_window(nums4, 2)); // [5,4,3,2]
print_vec(&sol.max_sliding_window(nums5, 2)); // [2,3,4,5]
print_vec(&sol.max_sliding_window(nums6, 3)); // [2,2]
}
use std::collections::VecDeque;
pub struct Solution;
impl Solution {
pub fn max_sliding_window(nums: Vec<i32>, k: i32) -> Vec<i32> {
let n = nums.len();
let k = k as usize;
if n == 0 || k == 0 || k > n {
return vec![];
}
let mut deq = VecDeque::new();
let mut result = Vec::with_capacity(n - k + 1);
for i in 0..n {
if !deq.is_empty() && deq.front() == Some(&(i - k)) {
deq.pop_front();
}
while !deq.is_empty() && nums[*deq.back().unwrap()] <= nums[i] {
deq.pop_back();
}
deq.push_back(i);
if i >= k - 1 {
result.push(nums[*deq.front().unwrap()]);
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
fn print_vec(vec: &[i32]) {
print!("[");
for (i, &val) in vec.iter().enumerate() {
print!("{}", val);
if i < vec.len() - 1 {
print!(",");
}
}
println!("]");
}
#[test]
fn test_max_sliding_window() {
let sol = Solution;
let nums1 = vec![1, 3, -1, -3, 5, 3, 6, 7];
let nums2 = vec![1];
let nums3 = vec![1, -1];
let nums4 = vec![5, 4, 3, 2, 1];
let nums5 = vec![1, 2, 3, 4, 5];
let nums6 = vec![2, 2, 2, 2];
let nums7: Vec<i32> = vec![];
print_vec(&sol.max_sliding_window(nums1, 3));
print_vec(&sol.max_sliding_window(nums2, 1));
print_vec(&sol.max_sliding_window(nums3, 1));
print_vec(&sol.max_sliding_window(nums7, 0));
print_vec(&sol.max_sliding_window(nums4, 2));
print_vec(&sol.max_sliding_window(nums5, 2));
print_vec(&sol.max_sliding_window(nums6, 3));
}
}
fn main() {
let sol = Solution;
let nums1 = vec![1, 3, -1, -3, 5, 3, 6, 7];
let nums2 = vec![1];
let nums3 = vec![1, -1];
let nums4 = vec![5, 4, 3, 2, 1];
let nums5 = vec![1, 2, 3, 4, 5];
let nums6 = vec![2, 2, 2, 2];
let nums7: Vec<i32> = vec![];
fn print_vec(vec: &[i32]) {
print!("[");
for (i, &val) in vec.iter().enumerate() {
print!("{}", val);
if i < vec.len() - 1 {
print!(",");
}
}
println!("]");
}
print_vec(&sol.max_sliding_window(nums1, 3));
print_vec(&sol.max_sliding_window(nums2, 1));
print_vec(&sol.max_sliding_window(nums3, 1));
print_vec(&sol.max_sliding_window(nums7, 0));
print_vec(&sol.max_sliding_window(nums4, 2));
print_vec(&sol.max_sliding_window(nums5, 2));
print_vec(&sol.max_sliding_window(nums6, 3));
}
// File: rust.optimized.rs
// Optimized Sliding Window Maximum using Monotonic Deque
// O(n) time, O(k) space
/// Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
/// of the max in the current window. For each index i:
/// - Pop front if index is out of current window (i - k).
/// - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
/// - Push i to back.
/// - If i >= k-1, add nums[front] to result (current window max).
/// This ensures O(n) time as each element is pushed/popped at most once.
///
/// Why this solution:
/// - Eliminates redundant max computations by maintaining only potential max candidates.
/// - Deque ensures front is the max, and back is kept in decreasing order.
/// - Outperforms max-heap (O(n log k)) for large inputs.
///
/// Where to use:
/// - Large inputs (n,k up to 10^5) where O(nk) would timeout.
/// - Streaming data scenarios requiring real-time max in fixed windows.
/// - When O(n) time is critical for performance.
///
/// Complexity:
/// - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
/// - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
///
/// Interview notes:
/// - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
/// - Why indices: "To check if front index is in window [i-k+1, i]."
/// - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
/// - Compare to brute: "O(nk) to O(n), critical for n=10^5."
/// - Rust note: Uses VecDeque for O(1) front/back operations, leveraging Rust's memory safety.
/// - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
/// - Amortized analysis: "Each index in/out once, total 2n operations."
///
/// Edge cases handled:
/// - Empty array or k=0: Return empty vector.
/// - k=1: Each element is max, deque size 1 per iteration.
/// - k=n: Single window, deque builds to global max.
/// - Decreasing sequence: Deque grows to k, front always largest.
/// - Increasing sequence: Frequent pops, deque often size 1 (latest max).
/// - Duplicates: Pop if >= to keep leftmost index for correct window timing.
/// - Negatives/zeros: Comparisons handle all values correctly.
/// - All equal elements: Pop >= keeps deque size 1, slides correctly.
///
/// ASCII Example for nums=[1,3,-1,-3,5], k=3:
/// i=0: deq=[] → push 0 → deq=[0] (1)
/// i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
/// i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
/// i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
/// i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
///
use std::collections::VecDeque;
pub struct Solution;
impl Solution {
pub fn max_sliding_window(nums: Vec<i32>, k: i32) -> Vec<i32> {
let n = nums.len();
let k = k as usize;
if n == 0 || k == 0 || k > n {
return vec![];
}
let mut deq = VecDeque::new(); // Deque of indices
let mut result = Vec::with_capacity(n - k + 1);
for i in 0..n {
// Remove out-of-window front
if !deq.is_empty() && deq.front() == Some(&(i - k)) {
deq.pop_front();
}
// Remove smaller or equal from back
while !deq.is_empty() && nums[*deq.back().unwrap()] <= nums[i] {
deq.pop_back();
}
// Add current index
deq.push_back(i);
// Add max to result if window is full
if i >= k - 1 {
result.push(nums[*deq.front().unwrap()]);
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
fn print_vec(vec: &[i32]) {
print!("[");
for (i, &val) in vec.iter().enumerate() {
print!("{}", val);
if i < vec.len() - 1 {
print!(",");
}
}
println!("]");
}
#[test]
fn test_max_sliding_window() {
let sol = Solution;
let nums1 = vec![1, 3, -1, -3, 5, 3, 6, 7];
let nums2 = vec![1];
let nums3 = vec![1, -1];
let nums4 = vec![5, 4, 3, 2, 1];
let nums5 = vec![1, 2, 3, 4, 5];
let nums6 = vec![2, 2, 2, 2];
let nums7: Vec<i32> = vec![];
print_vec(&sol.max_sliding_window(nums1, 3)); // [3,3,5,5,6,7]
print_vec(&sol.max_sliding_window(nums2, 1)); // [1]
print_vec(&sol.max_sliding_window(nums3, 1)); // [1,-1]
print_vec(&sol.max_sliding_window(nums7, 0)); // []
print_vec(&sol.max_sliding_window(nums4, 2)); // [5,4,3,2]
print_vec(&sol.max_sliding_window(nums5, 2)); // [2,3,4,5]
print_vec(&sol.max_sliding_window(nums6, 3)); // [2,2]
}
}
fn main() {
let sol = Solution;
let nums1 = vec![1, 3, -1, -3, 5, 3, 6, 7];
let nums2 = vec![1];
let nums3 = vec![1, -1];
let nums4 = vec![5, 4, 3, 2, 1];
let nums5 = vec![1, 2, 3, 4, 5];
let nums6 = vec![2, 2, 2, 2];
let nums7: Vec<i32> = vec![];
fn print_vec(vec: &[i32]) {
print!("[");
for (i, &val) in vec.iter().enumerate() {
print!("{}", val);
if i < vec.len() - 1 {
print!(",");
}
}
println!("]");
}
print_vec(&sol.max_sliding_window(nums1, 3)); // [3,3,5,5,6,7]
print_vec(&sol.max_sliding_window(nums2, 1)); // [1]
print_vec(&sol.max_sliding_window(nums3, 1)); // [1,-1]
print_vec(&sol.max_sliding_window(nums7, 0)); // []
print_vec(&sol.max_sliding_window(nums4, 2)); // [5,4,3,2]
print_vec(&sol.max_sliding_window(nums5, 2)); // [2,3,4,5]
print_vec(&sol.max_sliding_window(nums6, 3)); // [2,2]
}
<?php
class Solution {
function maxSlidingWindow($nums, $k) {
$n = count($nums);
if ($n == 0 || $k == 0 || $k > $n) return [];
$result = [];
for ($i = 0; $i <= $n - $k; $i++) {
$maxVal = PHP_INT_MIN;
for ($j = $i; $j < $i + $k; $j++) {
$maxVal = max($maxVal, $nums[$j]);
}
$result[] = $maxVal;
}
return $result;
}
}
$sol = new Solution();
$nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
$nums2 = [1];
$nums3 = [1, -1];
$nums4 = [5, 4, 3, 2, 1];
$nums5 = [1, 2, 3, 4, 5];
$nums6 = [2, 2, 2, 2];
$nums7 = [];
function printArray($arr) {
echo "[" . implode(",", $arr) . "]\n";
}
printArray($sol->maxSlidingWindow($nums1, 3));
printArray($sol->maxSlidingWindow($nums2, 1));
printArray($sol->maxSlidingWindow($nums3, 1));
printArray($sol->maxSlidingWindow($nums7, 0));
printArray($sol->maxSlidingWindow($nums4, 2));
printArray($sol->maxSlidingWindow($nums5, 2));
printArray($sol->maxSlidingWindow($nums6, 3));
?>
<?php
// File: php.brute.php
// Brute-force Sliding Window Maximum
// O(n * k) time, O(1) extra space (n = count($nums))
/**
* Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
* This approach directly computes the max for each window by scanning, ensuring correctness but suffering
* from high time complexity due to redundant computations across overlapping windows.
*
* Why this solution:
* - Simplest implementation, requiring no additional data structures beyond the output array.
* - Ideal for interviews to demonstrate initial problem understanding before optimization.
* - Guarantees correctness for all inputs by recomputing max for each window.
*
* Where to use:
* - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
* - To validate optimized solutions during development or debugging.
* - When space is critical, though time complexity may cause timeouts for large inputs.
*
* Complexity:
* - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
* For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
* - Space: O(1) - Excluding output array, only uses a few variables.
*
* Interview notes:
* - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
* - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
* - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
* - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
* - Handles all cases correctly since it recomputes fresh for each window.
* - PHP note: Uses PHP_INT_MIN for smallest int, array operations are straightforward.
*
* Edge cases handled:
* - Empty array or k=0: Returns empty array.
* - k=1: Returns the input array, as each element is its own max.
* - k=n: Returns a single element, the global max.
* - Decreasing sequence: Max is always leftmost in window.
* - Increasing sequence: Max is always rightmost.
* - All equal elements: Returns the same value for all windows.
* - Negatives/zeros: Comparisons handle all values correctly.
*
* ASCII Example for nums=[1,3,-1], k=3:
* Window 0-2: [1,3,-1]
* - Scan: 1, 3 (max so far), -1 < 3 → max = 3
* Output: [3]
*
* ASCII for nums=[5,4,3], k=2:
* Window 0-1: [5,4] → max = 5
* Window 1-2: [4,3] → max = 4
* Output: [5,4]
*
* @param array $nums Input array of integers
* @param int $k Sliding window size
* @return array Array of maximums for each window
*/
class Solution {
/**
* @param Integer[] $nums
* @param Integer $k
* @return Integer[]
*/
function maxSlidingWindow($nums, $k) {
$n = count($nums);
if ($n == 0 || $k == 0 || $k > $n) return []; // Handle invalid/empty cases
$result = [];
for ($i = 0; $i <= $n - $k; $i++) { // Loop over window starts
$maxVal = PHP_INT_MIN; // Initialize to smallest possible
for ($j = $i; $j < $i + $k; $j++) { // Scan window
$maxVal = max($maxVal, $nums[$j]); // Update max
}
$result[] = $maxVal; // Add to result
}
return $result;
}
}
// Test runner
$sol = new Solution();
$nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
$nums2 = [1];
$nums3 = [1, -1];
$nums4 = [5, 4, 3, 2, 1];
$nums5 = [1, 2, 3, 4, 5];
$nums6 = [2, 2, 2, 2];
$nums7 = [];
function printArray($arr) {
echo "[" . implode(",", $arr) . "]\n";
}
printArray($sol->maxSlidingWindow($nums1, 3)); // [3,3,5,5,6,7]
printArray($sol->maxSlidingWindow($nums2, 1)); // [1]
printArray($sol->maxSlidingWindow($nums3, 1)); // [1,-1]
printArray($sol->maxSlidingWindow($nums7, 0)); // []
printArray($sol->maxSlidingWindow($nums4, 2)); // [5,4,3,2]
printArray($sol->maxSlidingWindow($nums5, 2)); // [2,3,4,5]
printArray($sol->maxSlidingWindow($nums6, 3)); // [2,2]
?>
<?php
class Solution {
function maxSlidingWindow($nums, $k) {
$n = count($nums);
if ($n == 0 || $k == 0 || $k > $n) return [];
$deq = new SplDoublyLinkedList();
$result = [];
for ($i = 0; $i < $n; $i++) {
if (!$deq->isEmpty() && $deq->bottom() == $i - $k) {
$deq->shift();
}
while (!$deq->isEmpty() && $nums[$deq->top()] <= $nums[$i]) {
$deq->pop();
}
$deq->push($i);
if ($i >= $k - 1) {
$result[] = $nums[$deq->bottom()];
}
}
return $result;
}
}
$sol = new Solution();
$nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
$nums2 = [1];
$nums3 = [1, -1];
$nums4 = [5, 4, 3, 2, 1];
$nums5 = [1, 2, 3, 4, 5];
$nums6 = [2, 2, 2, 2];
$nums7 = [];
function printArray($arr) {
echo "[" . implode(",", $arr) . "]\n";
}
printArray($sol->maxSlidingWindow($nums1, 3));
printArray($sol->maxSlidingWindow($nums2, 1));
printArray($sol->maxSlidingWindow($nums3, 1));
printArray($sol->maxSlidingWindow($nums7, 0));
printArray($sol->maxSlidingWindow($nums4, 2));
printArray($sol->maxSlidingWindow($nums5, 2));
printArray($sol->maxSlidingWindow($nums6, 3));
?>
<?php
// File: php.optimized.php
// Optimized Sliding Window Maximum using Monotonic Deque
// O(n) time, O(k) space
/**
* Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
* of the max in the current window. For each index i:
* - Pop front if index is out of current window (i - k).
* - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
* - Push i to back.
* - If i >= k-1, add nums[front] to result (current window max).
* This ensures O(n) time as each element is pushed/popped at most once.
*
* Why this solution:
* - Eliminates redundant max computations by maintaining only potential max candidates.
* - Deque ensures front is the max, and back is kept in decreasing order.
* - Outperforms max-heap (O(n log k)) for large inputs.
*
* Where to use:
* - Large inputs (n,k up to 10^5) where O(nk) would timeout.
* - Streaming data scenarios requiring real-time max in fixed windows.
* - When O(n) time is critical for performance.
*
* Complexity:
* - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
* - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
*
* Interview notes:
* - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
* - Why indices: "To check if front index is in window [i-k+1, i]."
* - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
* - Compare to brute: "O(nk) to O(n), critical for n=10^5."
* - PHP note: Uses SplDoublyLinkedList for O(1) front/back operations, unlike array shift().
* - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
* - Amortized analysis: "Each index in/out once, total 2n operations."
*
* Edge cases handled:
* - Empty array or k=0: Return empty array.
* - k=1: Each element is max, deque size 1 per iteration.
* - k=n: Single window, deque builds to global max.
* - Decreasing sequence: Deque grows to k, front always largest.
* - Increasing sequence: Frequent pops, deque often size 1 (latest max).
* - Duplicates: Pop if >= to keep leftmost index for correct window timing.
* - Negatives/zeros: Comparisons handle all values correctly.
* - All equal elements: Pop >= keeps deque size 1, slides correctly.
*
* ASCII Example for nums=[1,3,-1,-3,5], k=3:
* i=0: deq=[] → push 0 → deq=[0] (1)
* i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
* i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
* i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
* i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
*
* @param array $nums Input array of integers
* @param int $k Sliding window size
* @return array Array of maximums for each window
*/
class Solution {
/**
* @param Integer[] $nums
* @param Integer $k
* @return Integer[]
*/
function maxSlidingWindow($nums, $k) {
$n = count($nums);
if ($n == 0 || $k == 0 || $k > $n) return [];
$deq = new SplDoublyLinkedList(); // Deque of indices
$result = [];
for ($i = 0; $i < $n; $i++) {
// Remove out-of-window front
if (!$deq->isEmpty() && $deq->bottom() == $i - $k) {
$deq->shift();
}
// Remove smaller or equal from back
while (!$deq->isEmpty() && $nums[$deq->top()] <= $nums[$i]) {
$deq->pop();
}
// Add current index
$deq->push($i);
// Add max to result if window is full
if ($i >= $k - 1) {
$result[] = $nums[$deq->bottom()];
}
}
return $result;
}
}
// Test runner
$sol = new Solution();
$nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
$nums2 = [1];
$nums3 = [1, -1];
$nums4 = [5, 4, 3, 2, 1];
$nums5 = [1, 2, 3, 4, 5];
$nums6 = [2, 2, 2, 2];
$nums7 = [];
function printArray($arr) {
echo "[" . implode(",", $arr) . "]\n";
}
printArray($sol->maxSlidingWindow($nums1, 3)); // [3,3,5,5,6,7]
printArray($sol->maxSlidingWindow($nums2, 1)); // [1]
printArray($sol->maxSlidingWindow($nums3, 1)); // [1,-1]
printArray($sol->maxSlidingWindow($nums7, 0)); // []
printArray($sol->maxSlidingWindow($nums4, 2)); // [5,4,3,2]
printArray($sol->maxSlidingWindow($nums5, 2)); // [2,3,4,5]
printArray($sol->maxSlidingWindow($nums6, 3)); // [2,2]
?>
class Solution {
List<int> maxSlidingWindow(List<int> nums, int k) {
final n = nums.length;
if (n == 0 || k == 0 || k > n) return [];
final result = <int>[];
for (var i = 0; i <= n - k; i++) {
var maxVal = -2147483648;
for (var j = i; j < i + k; j++) {
if (nums[j] > maxVal) {
maxVal = nums[j];
}
}
result.add(maxVal);
}
return result;
}
}
void main() {
final sol = Solution();
final nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
final nums2 = [1];
final nums3 = [1, -1];
final nums4 = [5, 4, 3, 2, 1];
final nums5 = [1, 2, 3, 4, 5];
final nums6 = [2, 2, 2, 2];
final nums7 = <int>[];
void printList(List<int> arr) {
print('[$arr]');
}
printList(sol.maxSlidingWindow(nums1, 3));
printList(sol.maxSlidingWindow(nums2, 1));
printList(sol.maxSlidingWindow(nums3, 1));
printList(sol.maxSlidingWindow(nums7, 0));
printList(sol.maxSlidingWindow(nums4, 2));
printList(sol.maxSlidingWindow(nums5, 2));
printList(sol.maxSlidingWindow(nums6, 3));
}
// File: dart.brute.dart
// Brute-force Sliding Window Maximum
// O(n * k) time, O(1) extra space (n = nums.length)
/**
* Brute-force approach: For each of the n - k + 1 windows, iterate over the k elements to find the maximum.
* This approach directly computes the max for each window by scanning, ensuring correctness but suffering
* from high time complexity due to redundant computations across overlapping windows.
*
* Why this solution:
* - Simplest implementation, requiring no additional data structures beyond the output list.
* - Ideal for interviews to demonstrate initial problem understanding before optimization.
* - Guarantees correctness for all inputs by recomputing max for each window.
*
* Where to use:
* - Small inputs (n <= 1000, k <= 1000) where quadratic time is acceptable.
* - To validate optimized solutions during development or debugging.
* - When space is critical, though time complexity may cause timeouts for large inputs.
*
* Complexity:
* - Time: O(n * k) - Iterates over (n - k + 1) windows, each scanning k elements.
* For max constraints (n=10^5, k=10^5), this is ~10^10 operations, leading to timeout.
* - Space: O(1) - Excluding output list, only uses a few variables.
*
* Interview notes:
* - Start with this to show problem grasp: "We can scan each window to find the max, but it's O(nk)."
* - Highlight inefficiency: "Overlapping windows recompute maxes; for n=10^5, k=10^5, it's impractical."
* - Transition to optimized: "A monotonic deque can reduce to O(n) by tracking max candidates."
* - FAANG tip: Use this to set up discussion, then pivot to deque for large inputs.
* - Handles all cases correctly since it recomputes fresh for each window.
* - Dart note: List in Dart is efficient for iteration, and max comparison is straightforward.
*
* Edge cases handled:
* - Empty array or k=0: Returns empty list.
* - k=1: Returns the input array, as each element is its own max.
* - k=n: Returns a single element, the global max.
* - Decreasing sequence: Max is always leftmost in window.
* - Increasing sequence: Max is always rightmost.
* - All equal elements: Returns the same value for all windows.
* - Negatives/zeros: Comparisons handle all values correctly.
*
* ASCII Example for nums=[1,3,-1], k=3:
* Window 0-2: [1,3,-1]
* - Scan: 1, 3 (max so far), -1 < 3 → max = 3
* Output: [3]
*
* ASCII for nums=[5,4,3], k=2:
* Window 0-1: [5,4] → max = 5
* Window 1-2: [4,3] → max = 4
* Output: [5,4]
*
* @param nums Input list of integers
* @param k Sliding window size
* @return List of maximums for each window
*/
class Solution {
List<int> maxSlidingWindow(List<int> nums, int k) {
final n = nums.length;
if (n == 0 || k == 0 || k > n) return []; // Handle invalid/empty cases
final result = <int>[];
for (var i = 0; i <= n - k; i++) { // Loop over window starts
var maxVal = -2147483648; // Use smallest int (equivalent to -2^31)
for (var j = i; j < i + k; j++) { // Scan window
if (nums[j] > maxVal) {
maxVal = nums[j]; // Update max
}
}
result.add(maxVal); // Add to result
}
return result;
}
}
// Test runner
void main() {
final sol = Solution();
final nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
final nums2 = [1];
final nums3 = [1, -1];
final nums4 = [5, 4, 3, 2, 1];
final nums5 = [1, 2, 3, 4, 5];
final nums6 = [2, 2, 2, 2];
final nums7 = <int>[];
void printList(List<int> arr) {
print('[$arr]');
}
printList(sol.maxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
printList(sol.maxSlidingWindow(nums2, 1)); // [1]
printList(sol.maxSlidingWindow(nums3, 1)); // [1,-1]
printList(sol.maxSlidingWindow(nums7, 0)); // []
printList(sol.maxSlidingWindow(nums4, 2)); // [5,4,3,2]
printList(sol.maxSlidingWindow(nums5, 2)); // [2,3,4,5]
printList(sol.maxSlidingWindow(nums6, 3)); // [2,2]
}
class Solution {
List<int> maxSlidingWindow(List<int> nums, int k) {
final n = nums.length;
if (n == 0 || k == 0 || k > n) return [];
final deq = <int>[];
final result = <int>[];
for (var i = 0; i < n; i++) {
if (deq.isNotEmpty && deq.first == i - k) {
deq.removeAt(0);
}
while (deq.isNotEmpty && nums[deq.last] <= nums[i]) {
deq.removeLast();
}
deq.add(i);
if (i >= k - 1) {
result.add(nums[deq.first]);
}
}
return result;
}
}
void main() {
final sol = Solution();
final nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
final nums2 = [1];
final nums3 = [1, -1];
final nums4 = [5, 4, 3, 2, 1];
final nums5 = [1, 2, 3, 4, 5];
final nums6 = [2, 2, 2, 2];
final nums7 = <int>[];
void printList(List<int> arr) {
print('[$arr]');
}
printList(sol.maxSlidingWindow(nums1, 3));
printList(sol.maxSlidingWindow(nums2, 1));
printList(sol.maxSlidingWindow(nums3, 1));
printList(sol.maxSlidingWindow(nums7, 0));
printList(sol.maxSlidingWindow(nums4, 2));
printList(sol.maxSlidingWindow(nums5, 2));
printList(sol.maxSlidingWindow(nums6, 3));
}
// File: dart.optimized.dart
// Optimized Sliding Window Maximum using Monotonic Deque
// O(n) time, O(k) space
/**
* Optimized approach: Maintain a monotonic decreasing deque of indices, where the front is always the index
* of the max in the current window. For each index i:
* - Pop front if index is out of current window (i - k).
* - Pop back if nums[i] >= nums[back] (smaller/equal can't be max in future windows).
* - Push i to back.
* - If i >= k-1, add nums[front] to result (current window max).
* This ensures O(n) time as each element is pushed/popped at most once.
*
* Why this solution:
* - Eliminates redundant max computations by maintaining only potential max candidates.
* - Deque ensures front is the max, and back is kept in decreasing order.
* - Outperforms max-heap (O(n log k)) for large inputs.
*
* Where to use:
* - Large inputs (n,k up to 10^5) where O(nk) would timeout.
* - Streaming data scenarios requiring real-time max in fixed windows.
* - When O(n) time is critical for performance.
*
* Complexity:
* - Time: O(n) - Each index is pushed and popped exactly once (amortized O(1) per operation).
* - Space: O(k) - Deque can hold up to k indices in worst case (decreasing sequence).
*
* Interview notes:
* - Explain monotonic property: "Deque keeps indices in decreasing value order, front is max."
* - Why indices: "To check if front index is in window [i-k+1, i]."
* - Pop back rationale: "If nums[i] >= nums[back], back can't be max later, as i stays longer."
* - Compare to brute: "O(nk) to O(n), critical for n=10^5."
* - Dart note: List as deque has O(1) add/removeLast, but removeFirst is O(n); total operations still O(n).
* - FAANG tip: Discuss variants like minimum (increasing deque) or next greater (stack).
* - Amortized analysis: "Each index in/out once, total 2n operations."
*
* Edge cases handled:
* - Empty array or k=0: Return empty list.
* - k=1: Each element is max, deque size 1 per iteration.
* - k=n: Single window, deque builds to global max.
* - Decreasing sequence: Deque grows to k, front always largest.
* - Increasing sequence: Frequent pops, deque often size 1 (latest max).
* - Duplicates: Pop if >= to keep leftmost index for correct window timing.
* - Negatives/zeros: Comparisons handle all values correctly.
* - All equal elements: Pop >= keeps deque size 1, slides correctly.
*
* ASCII Example for nums=[1,3,-1,-3,5], k=3:
* i=0: deq=[] → push 0 → deq=[0] (1)
* i=1: 3>1 → pop 0, push 1 → deq=[1] (3)
* i=2: -1<3 → push 2 → deq=[1,2] (3,-1), result=[3]
* i=3: front=1>=0 OK, -3<-1 → push 3 → deq=[1,2,3], result=[3,3]
* i=4: front=1<2 pop 1, 5>-3 pop 3, 5>-1 pop 2, push 4 → deq=[4], result=[3,3,5]
*
* @param nums Input list of integers
* @param k Sliding window size
* @return List of maximums for each window
*/
class Solution {
List<int> maxSlidingWindow(List<int> nums, int k) {
final n = nums.length;
if (n == 0 || k == 0 || k > n) return [];
final deq = <int>[]; // List as deque
final result = <int>[];
for (var i = 0; i < n; i++) {
// Remove out-of-window front
if (deq.isNotEmpty && deq.first == i - k) {
deq.removeAt(0);
}
// Remove smaller or equal from back
while (deq.isNotEmpty && nums[deq.last] <= nums[i]) {
deq.removeLast();
}
// Add current index
deq.add(i);
// Add max to result if window is full
if (i >= k - 1) {
result.add(nums[deq.first]);
}
}
return result;
}
}
// Test runner
void main() {
final sol = Solution();
final nums1 = [1, 3, -1, -3, 5, 3, 6, 7];
final nums2 = [1];
final nums3 = [1, -1];
final nums4 = [5, 4, 3, 2, 1];
final nums5 = [1, 2, 3, 4, 5];
final nums6 = [2, 2, 2, 2];
final nums7 = <int>[];
void printList(List<int> arr) {
print('[$arr]');
}
printList(sol.maxSlidingWindow(nums1, 3)); // [3,3,5,5,6,7]
printList(sol.maxSlidingWindow(nums2, 1)); // [1]
printList(sol.maxSlidingWindow(nums3, 1)); // [1,-1]
printList(sol.maxSlidingWindow(nums7, 0)); // []
printList(sol.maxSlidingWindow(nums4, 2)); // [5,4,3,2]
printList(sol.maxSlidingWindow(nums5, 2)); // [2,3,4,5]
printList(sol.maxSlidingWindow(nums6, 3)); // [2,2]
}