questions M-R

This commit is contained in:
2025-05-25 12:43:25 +01:00
parent 5dbe52df0d
commit ddceeec07e
62 changed files with 12841 additions and 0 deletions

View File

@@ -0,0 +1,179 @@
title: Maximum Subarray
slug: maximum-subarray
difficulty: medium
leetcode_id: 53
leetcode_url: https://leetcode.com/problems/maximum-subarray/
categories:
- arrays
- dynamic-programming
patterns:
- dynamic-programming
function_signature: "def max_subarray(nums: list[int]) -> int:"
test_cases:
visible:
- input: { nums: [-2, 1, -3, 4, -1, 2, 1, -5, 4] }
expected: 6
- input: { nums: [1] }
expected: 1
- input: { nums: [5, 4, -1, 7, 8] }
expected: 23
hidden:
- input: { nums: [-1] }
expected: -1
- input: { nums: [-2, -1] }
expected: -1
- input: { nums: [1, 2, 3, 4] }
expected: 10
- input: { nums: [-1, -2, -3, -4] }
expected: -1
description: |
Given an integer array `nums`, find the subarray with the largest sum, and return *its sum*.
A **subarray** is a contiguous non-empty sequence of elements within an array.
constraints: |
- `1 <= nums.length <= 10^5`
- `-10^4 <= nums[i] <= 10^4`
examples:
- input: "nums = [-2,1,-3,4,-1,2,1,-5,4]"
output: "6"
explanation: "The subarray [4,-1,2,1] has the largest sum 6."
- input: "nums = [1]"
output: "1"
explanation: "The subarray [1] has the largest sum 1."
- input: "nums = [5,4,-1,7,8]"
output: "23"
explanation: "The entire array [5,4,-1,7,8] has the largest sum 23."
explanation:
intuition: |
Imagine you're walking along a number line, collecting values. At each step, you face a simple decision: should you keep accumulating, or cut your losses and start fresh?
Think of it like this: you're tracking a "running sum" as you traverse the array. If your running sum becomes negative, it's **dragging you down** — any future subarray would be better off starting fresh without that negative baggage.
The key insight of **Kadane's Algorithm** is that at each position `i`, you have exactly two choices:
1. **Extend** the previous subarray by adding `nums[i]` (if the previous sum helps)
2. **Start fresh** at `nums[i]` (if the previous sum was negative)
Mathematically: `current_sum = max(nums[i], current_sum + nums[i])`
We also track the maximum sum seen so far, because the optimal subarray might end before the last element.
approach: |
We solve this using **Kadane's Algorithm**:
**Step 1: Initialise tracking variables**
- `current_sum = nums[0]`: The sum of the current subarray ending at position i
- `max_sum = nums[0]`: The maximum sum we've found so far
- Starting with `nums[0]` (not 0) handles all-negative arrays correctly
&nbsp;
**Step 2: Iterate from index 1 to the end**
- For each element `nums[i]`:
- **Decide**: extend or restart? `current_sum = max(nums[i], current_sum + nums[i])`
- If `current_sum + nums[i] >= nums[i]`, extend (previous sum is non-negative)
- If `current_sum + nums[i] < nums[i]`, restart (previous sum was negative)
- **Update maximum**: `max_sum = max(max_sum, current_sum)`
&nbsp;
**Step 3: Return the result**
- Return `max_sum` — the largest subarray sum encountered
- Note: we don't return `current_sum` because the optimal subarray might have ended earlier
&nbsp;
This elegant algorithm makes the locally optimal choice at each step (extend or restart), which leads to the globally optimal solution.
common_pitfalls:
- title: Initialising max_sum to 0
description: |
If all elements are negative (e.g., `[-3, -2, -1]`), the maximum subarray sum is `-1`, not `0`.
Initialising `max_sum = 0` would incorrectly return `0` for such arrays, as if an empty subarray were valid (it's not — the problem requires a non-empty subarray).
Always initialise with `nums[0]` to guarantee a valid answer.
wrong_approach: "max_sum = 0"
correct_approach: "max_sum = nums[0]"
- title: Only Returning current_sum at the End
description: |
The maximum subarray doesn't necessarily include the last element! Consider `[4, -1, 2, 1, -5]`: the maximum sum is `6` (subarray `[4, -1, 2, 1]`), but `current_sum` at the end is `1`.
You must track `max_sum` throughout the traversal and update it whenever `current_sum` exceeds it.
wrong_approach: "return current_sum"
correct_approach: "Track max_sum and return it"
- title: Confusing Subarray with Subsequence
description: |
A **subarray** must be contiguous — you cannot skip elements. This is why Kadane's algorithm works: at each position, the subarray either extends from the previous position or starts fresh.
A **subsequence** allows skipping elements, which would require a different approach entirely.
wrong_approach: "Thinking you can skip elements"
correct_approach: "Subarray = contiguous sequence"
key_takeaways:
- "**Kadane's Algorithm**: The classic O(n) solution for maximum subarray — memorise it!"
- "**Local vs global optimum**: Sometimes making locally optimal choices (extend or restart) yields the global optimum"
- "**Initialisation matters**: Using `nums[0]` instead of `0` handles edge cases correctly"
- "**Pattern recognition**: This 'extend or restart' logic applies to many contiguous subarray problems"
time_complexity: "O(n). Single pass through the array, with O(1) work at each position."
space_complexity: "O(1). Only two variables (`current_sum` and `max_sum`) regardless of input size."
solutions:
- approach_name: Kadane's Algorithm
is_optimal: true
code: |
def max_subarray(nums: list[int]) -> int:
# Initialise with first element (handles all-negative arrays)
current_sum = nums[0]
max_sum = nums[0]
# Process remaining elements
for i in range(1, len(nums)):
# Decision: extend previous subarray or start fresh?
# Extend if previous sum helps, otherwise restart
current_sum = max(nums[i], current_sum + nums[i])
# Update maximum if current subarray is better
max_sum = max(max_sum, current_sum)
return max_sum
explanation: |
**Time Complexity:** O(n) — Single pass through the array.
**Space Complexity:** O(1) — Only two variables needed.
At each position, we decide whether to extend the previous subarray or start a new one. We track the maximum sum seen throughout the traversal. This greedy choice at each step produces the globally optimal result.
- approach_name: Brute Force
is_optimal: false
code: |
def max_subarray(nums: list[int]) -> int:
n = len(nums)
max_sum = nums[0]
# Try every possible starting point
for i in range(n):
current_sum = 0
# Try every possible ending point from i
for j in range(i, n):
current_sum += nums[j]
max_sum = max(max_sum, current_sum)
return max_sum
explanation: |
**Time Complexity:** O(n²) — Nested loops checking all subarrays.
**Space Complexity:** O(1) — Only tracking sums.
This approach tries every possible subarray by fixing a start index and extending to each possible end index. While correct, it's too slow for large inputs (up to 10 billion operations for n = 10^5).