medium array/string questions
This commit is contained in:
99
backend/data/questions/container-with-most-water.yaml
Normal file
99
backend/data/questions/container-with-most-water.yaml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
title: Container With Most Water
|
||||||
|
slug: container-with-most-water
|
||||||
|
difficulty: medium
|
||||||
|
leetcode_id: 11
|
||||||
|
leetcode_url: https://leetcode.com/problems/container-with-most-water/
|
||||||
|
categories:
|
||||||
|
- arrays
|
||||||
|
- two-pointers
|
||||||
|
patterns:
|
||||||
|
- two-pointers
|
||||||
|
- greedy
|
||||||
|
|
||||||
|
description: |
|
||||||
|
You are given an integer array `height` of length n. There are n vertical lines drawn such
|
||||||
|
that the two endpoints of the ith line are (i, 0) and (i, height[i]).
|
||||||
|
|
||||||
|
Find two lines that together with the x-axis form a container, such that the container
|
||||||
|
contains the most water.
|
||||||
|
|
||||||
|
Return the maximum amount of water a container can store.
|
||||||
|
|
||||||
|
constraints: |
|
||||||
|
- n == height.length
|
||||||
|
- 2 <= n <= 10^5
|
||||||
|
- 0 <= height[i] <= 10^4
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- input: "height = [1,8,6,2,5,4,8,3,7]"
|
||||||
|
output: "49"
|
||||||
|
explanation: "Lines at index 1 (height 8) and 8 (height 7) form container with area 7 * 7 = 49."
|
||||||
|
- input: "height = [1,1]"
|
||||||
|
output: "1"
|
||||||
|
explanation: "Only container possible has area 1 * 1 = 1."
|
||||||
|
|
||||||
|
explanation:
|
||||||
|
approach: |
|
||||||
|
1. Start with two pointers at the far left and far right
|
||||||
|
2. Calculate the area formed by the two lines
|
||||||
|
3. Move the pointer pointing to the shorter line inward
|
||||||
|
4. Track maximum area seen
|
||||||
|
5. Continue until pointers meet
|
||||||
|
|
||||||
|
intuition: |
|
||||||
|
Area = width × height. The height is limited by the shorter line.
|
||||||
|
|
||||||
|
Starting with maximum width (endpoints), we can only improve by finding taller lines.
|
||||||
|
If we move the taller pointer, width decreases and height can't increase beyond the
|
||||||
|
shorter line — so area can only decrease or stay the same.
|
||||||
|
|
||||||
|
Moving the shorter pointer gives us a chance to find a taller line that could
|
||||||
|
increase the height enough to compensate for the reduced width.
|
||||||
|
|
||||||
|
common_pitfalls:
|
||||||
|
- title: Moving the wrong pointer
|
||||||
|
description: |
|
||||||
|
Always move the pointer pointing to the shorter line. Moving the taller one
|
||||||
|
cannot possibly increase the area since height is constrained by the shorter.
|
||||||
|
wrong_approach: "Moving left pointer always or randomly"
|
||||||
|
correct_approach: "Move the pointer with smaller height[i]"
|
||||||
|
|
||||||
|
- title: Using nested loops
|
||||||
|
description: |
|
||||||
|
O(n²) brute force is unnecessary. Two pointers achieve O(n) by eliminating
|
||||||
|
suboptimal pairs without checking them.
|
||||||
|
|
||||||
|
key_takeaways:
|
||||||
|
- Two pointers from opposite ends is powerful for optimization
|
||||||
|
- Moving the limiting factor gives the best chance of improvement
|
||||||
|
- Greedy choice (move shorter) is provably optimal here
|
||||||
|
- Width × min(heights) is the key formula
|
||||||
|
|
||||||
|
time_complexity: "O(n)"
|
||||||
|
space_complexity: "O(1)"
|
||||||
|
complexity_explanation: |
|
||||||
|
Time: Each pointer moves at most n times total.
|
||||||
|
Space: Only a few variables for pointers and max area.
|
||||||
|
|
||||||
|
solutions:
|
||||||
|
- approach_name: Two Pointers (Optimal)
|
||||||
|
is_optimal: true
|
||||||
|
code: |
|
||||||
|
def max_area(height: list[int]) -> int:
|
||||||
|
left, right = 0, len(height) - 1
|
||||||
|
max_water = 0
|
||||||
|
|
||||||
|
while left < right:
|
||||||
|
width = right - left
|
||||||
|
h = min(height[left], height[right])
|
||||||
|
max_water = max(max_water, width * h)
|
||||||
|
|
||||||
|
if height[left] < height[right]:
|
||||||
|
left += 1
|
||||||
|
else:
|
||||||
|
right -= 1
|
||||||
|
|
||||||
|
return max_water
|
||||||
|
explanation: |
|
||||||
|
Start from both ends and move the shorter line inward.
|
||||||
|
Track maximum area found during traversal.
|
||||||
114
backend/data/questions/longest-substring-without-repeating.yaml
Normal file
114
backend/data/questions/longest-substring-without-repeating.yaml
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
title: Longest Substring Without Repeating Characters
|
||||||
|
slug: longest-substring-without-repeating
|
||||||
|
difficulty: medium
|
||||||
|
leetcode_id: 3
|
||||||
|
leetcode_url: https://leetcode.com/problems/longest-substring-without-repeating-characters/
|
||||||
|
categories:
|
||||||
|
- strings
|
||||||
|
- hash-tables
|
||||||
|
patterns:
|
||||||
|
- sliding-window
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Given a string `s`, find the length of the longest substring without repeating characters.
|
||||||
|
|
||||||
|
constraints: |
|
||||||
|
- 0 <= s.length <= 5 * 10^4
|
||||||
|
- s consists of English letters, digits, symbols and spaces
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- input: 's = "abcabcbb"'
|
||||||
|
output: "3"
|
||||||
|
explanation: "The answer is 'abc', with length 3."
|
||||||
|
- input: 's = "bbbbb"'
|
||||||
|
output: "1"
|
||||||
|
explanation: "The answer is 'b', with length 1."
|
||||||
|
- input: 's = "pwwkew"'
|
||||||
|
output: "3"
|
||||||
|
explanation: "The answer is 'wke', with length 3."
|
||||||
|
|
||||||
|
explanation:
|
||||||
|
approach: |
|
||||||
|
1. Use a sliding window with left and right pointers
|
||||||
|
2. Maintain a set of characters in the current window
|
||||||
|
3. Expand right pointer, adding characters to the set
|
||||||
|
4. When a duplicate is found, shrink from left until duplicate is removed
|
||||||
|
5. Track maximum window size throughout
|
||||||
|
|
||||||
|
intuition: |
|
||||||
|
We're looking for the longest contiguous substring where all characters are unique.
|
||||||
|
A sliding window naturally represents a substring.
|
||||||
|
|
||||||
|
When we encounter a duplicate, the current window is invalid. Instead of restarting
|
||||||
|
from scratch, we shrink the window from the left until the duplicate is removed.
|
||||||
|
This way, we never revisit characters unnecessarily.
|
||||||
|
|
||||||
|
common_pitfalls:
|
||||||
|
- title: Resetting the window incorrectly
|
||||||
|
description: |
|
||||||
|
When finding a duplicate, don't start over from the next character.
|
||||||
|
Instead, shrink from the left until the duplicate is removed.
|
||||||
|
wrong_approach: "left = right when duplicate found"
|
||||||
|
correct_approach: "Increment left until duplicate is out of window"
|
||||||
|
|
||||||
|
- title: Not handling empty string
|
||||||
|
description: |
|
||||||
|
An empty string should return 0. Make sure the algorithm handles this.
|
||||||
|
|
||||||
|
- title: Off-by-one in length calculation
|
||||||
|
description: |
|
||||||
|
Window length is right - left + 1, or just track max before removing.
|
||||||
|
|
||||||
|
key_takeaways:
|
||||||
|
- Sliding window is ideal for substring problems with constraints
|
||||||
|
- Use a set or map to track elements in current window
|
||||||
|
- Shrinking from one end maintains the contiguous property
|
||||||
|
- This pattern appears in many "longest/shortest with constraint" problems
|
||||||
|
|
||||||
|
time_complexity: "O(n)"
|
||||||
|
space_complexity: "O(min(m, n))"
|
||||||
|
complexity_explanation: |
|
||||||
|
Time: Each character is visited at most twice (once by right, once by left).
|
||||||
|
Space: The set holds at most min(n, m) characters where m is the charset size.
|
||||||
|
|
||||||
|
solutions:
|
||||||
|
- approach_name: Sliding Window with Set (Optimal)
|
||||||
|
is_optimal: true
|
||||||
|
code: |
|
||||||
|
def length_of_longest_substring(s: str) -> int:
|
||||||
|
char_set = set()
|
||||||
|
left = 0
|
||||||
|
max_length = 0
|
||||||
|
|
||||||
|
for right in range(len(s)):
|
||||||
|
while s[right] in char_set:
|
||||||
|
char_set.remove(s[left])
|
||||||
|
left += 1
|
||||||
|
|
||||||
|
char_set.add(s[right])
|
||||||
|
max_length = max(max_length, right - left + 1)
|
||||||
|
|
||||||
|
return max_length
|
||||||
|
explanation: |
|
||||||
|
Expand window by moving right pointer.
|
||||||
|
When duplicate found, shrink from left until window is valid again.
|
||||||
|
|
||||||
|
- approach_name: Optimized with Hash Map
|
||||||
|
is_optimal: true
|
||||||
|
code: |
|
||||||
|
def length_of_longest_substring(s: str) -> int:
|
||||||
|
char_index = {}
|
||||||
|
left = 0
|
||||||
|
max_length = 0
|
||||||
|
|
||||||
|
for right, char in enumerate(s):
|
||||||
|
if char in char_index and char_index[char] >= left:
|
||||||
|
left = char_index[char] + 1
|
||||||
|
|
||||||
|
char_index[char] = right
|
||||||
|
max_length = max(max_length, right - left + 1)
|
||||||
|
|
||||||
|
return max_length
|
||||||
|
explanation: |
|
||||||
|
Store the last index of each character.
|
||||||
|
Jump left pointer directly past the duplicate instead of shrinking one by one.
|
||||||
119
backend/data/questions/three-sum.yaml
Normal file
119
backend/data/questions/three-sum.yaml
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
title: 3Sum
|
||||||
|
slug: three-sum
|
||||||
|
difficulty: medium
|
||||||
|
leetcode_id: 15
|
||||||
|
leetcode_url: https://leetcode.com/problems/3sum/
|
||||||
|
categories:
|
||||||
|
- arrays
|
||||||
|
- two-pointers
|
||||||
|
- sorting
|
||||||
|
patterns:
|
||||||
|
- two-pointers
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Given an integer array `nums`, return all the triplets [nums[i], nums[j], nums[k]] such that
|
||||||
|
i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
|
||||||
|
|
||||||
|
Notice that the solution set must not contain duplicate triplets.
|
||||||
|
|
||||||
|
constraints: |
|
||||||
|
- 3 <= nums.length <= 3000
|
||||||
|
- -10^5 <= nums[i] <= 10^5
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- input: "nums = [-1,0,1,2,-1,-4]"
|
||||||
|
output: "[[-1,-1,2],[-1,0,1]]"
|
||||||
|
explanation: "The distinct triplets that sum to zero."
|
||||||
|
- input: "nums = [0,1,1]"
|
||||||
|
output: "[]"
|
||||||
|
explanation: "No triplet sums to zero."
|
||||||
|
- input: "nums = [0,0,0]"
|
||||||
|
output: "[[0,0,0]]"
|
||||||
|
explanation: "Only one triplet sums to zero."
|
||||||
|
|
||||||
|
explanation:
|
||||||
|
approach: |
|
||||||
|
1. Sort the array
|
||||||
|
2. For each element nums[i], find pairs that sum to -nums[i]
|
||||||
|
3. Use two pointers (left, right) to find pairs in the remaining array
|
||||||
|
4. Skip duplicates at each level to avoid duplicate triplets
|
||||||
|
|
||||||
|
intuition: |
|
||||||
|
After sorting, for each fixed element nums[i], we need to find nums[j] + nums[k] = -nums[i].
|
||||||
|
This reduces to the Two Sum II problem on a sorted array, solvable with two pointers.
|
||||||
|
|
||||||
|
Sorting enables two things: efficient two-pointer search and easy duplicate skipping.
|
||||||
|
We skip duplicates by checking if current value equals previous value.
|
||||||
|
|
||||||
|
common_pitfalls:
|
||||||
|
- title: Not handling duplicates
|
||||||
|
description: |
|
||||||
|
Without duplicate skipping, you'll return duplicate triplets.
|
||||||
|
Skip at the outer loop and both pointers.
|
||||||
|
wrong_approach: "Not skipping when nums[i] == nums[i-1]"
|
||||||
|
correct_approach: "if i > 0 and nums[i] == nums[i-1]: continue"
|
||||||
|
|
||||||
|
- title: Wrong pointer skipping
|
||||||
|
description: |
|
||||||
|
After finding a valid triplet, skip duplicates for both left and right pointers
|
||||||
|
while maintaining left < right.
|
||||||
|
|
||||||
|
- title: Starting i too late
|
||||||
|
description: |
|
||||||
|
The outer loop should start at index 0. Also, skip if nums[i] > 0 since
|
||||||
|
sorted array means no valid triplet possible.
|
||||||
|
|
||||||
|
key_takeaways:
|
||||||
|
- Reduce N-sum to (N-1)-sum by fixing one element
|
||||||
|
- Sorting enables two-pointer approach and duplicate handling
|
||||||
|
- Duplicate skipping happens at multiple levels
|
||||||
|
- Time complexity is O(n²) — can't do better for returning all triplets
|
||||||
|
|
||||||
|
time_complexity: "O(n²)"
|
||||||
|
space_complexity: "O(log n) to O(n)"
|
||||||
|
complexity_explanation: |
|
||||||
|
Time: O(n log n) for sorting + O(n²) for the two-pointer search.
|
||||||
|
Space: Depends on sorting algorithm (log n for in-place, n for non-in-place).
|
||||||
|
|
||||||
|
solutions:
|
||||||
|
- approach_name: Sort + Two Pointers (Optimal)
|
||||||
|
is_optimal: true
|
||||||
|
code: |
|
||||||
|
def three_sum(nums: list[int]) -> list[list[int]]:
|
||||||
|
nums.sort()
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for i in range(len(nums) - 2):
|
||||||
|
# Skip duplicates for i
|
||||||
|
if i > 0 and nums[i] == nums[i - 1]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Early termination
|
||||||
|
if nums[i] > 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
left, right = i + 1, len(nums) - 1
|
||||||
|
|
||||||
|
while left < right:
|
||||||
|
total = nums[i] + nums[left] + nums[right]
|
||||||
|
|
||||||
|
if total < 0:
|
||||||
|
left += 1
|
||||||
|
elif total > 0:
|
||||||
|
right -= 1
|
||||||
|
else:
|
||||||
|
result.append([nums[i], nums[left], nums[right]])
|
||||||
|
|
||||||
|
# Skip duplicates for left and right
|
||||||
|
while left < right and nums[left] == nums[left + 1]:
|
||||||
|
left += 1
|
||||||
|
while left < right and nums[right] == nums[right - 1]:
|
||||||
|
right -= 1
|
||||||
|
|
||||||
|
left += 1
|
||||||
|
right -= 1
|
||||||
|
|
||||||
|
return result
|
||||||
|
explanation: |
|
||||||
|
Fix one element and use two pointers to find the other two.
|
||||||
|
Skip duplicates at all levels to avoid duplicate triplets.
|
||||||
Reference in New Issue
Block a user