Medium: Arrays & Strings (11-20)
Medium array and string problems are the most common interview questions at AI/ML companies. These ten problems cover two pointers, sliding window, hash maps, sorting, and prefix sums — the patterns that account for roughly 40% of all coding interview questions.
Problem 11: 3Sum
LeetCode #15 — Given an integer array, return all unique triplets [nums[i], nums[j], nums[k]] such that i != j != k and nums[i] + nums[j] + nums[k] == 0.
Pattern: Sort + Two Pointers — Fix one element, use two pointers on the remaining sorted array.
ML Context: Feature interaction search in automated feature engineering uses similar combinatorial search patterns with pruning to avoid redundant combinations.
def threeSum(nums):
"""
Time: O(n^2) - sort O(n log n) + two pointer scan O(n) per element
Space: O(1) extra (excluding output)
"""
nums.sort()
result = []
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]: # skip duplicates
continue
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]])
while left < right and nums[left] == nums[left + 1]:
left += 1 # skip duplicates
while left < right and nums[right] == nums[right - 1]:
right -= 1 # skip duplicates
left += 1
right -= 1
return result
# threeSum([-1,0,1,2,-1,-4]) -> [[-1,-1,2],[-1,0,1]]
Problem 12: Container With Most Water
LeetCode #11 — Given n non-negative integers height where each represents a point (i, height[i]), find two lines that form a container holding the most water.
Pattern: Two Pointers — Start at widest container, shrink from the shorter side.
ML Context: Optimization problems where you maximize an objective by adjusting two parameters from boundary conditions inward appear in hyperparameter grid search and constraint satisfaction.
def maxArea(height):
"""
Time: O(n) - two pointers meet in the middle
Space: O(1)
"""
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
# maxArea([1,8,6,2,5,4,8,3,7]) -> 49
Problem 13: Longest Substring Without Repeating Characters
LeetCode #3 — Given a string s, find the length of the longest substring without repeating characters.
Pattern: Sliding Window + Hash Map — Expand right, shrink left when a duplicate is found.
ML Context: Sliding windows are used extensively in NLP tokenization (subword segmentation), time-series feature extraction, and convolutional operations over sequences.
def lengthOfLongestSubstring(s):
"""
Time: O(n) - each character visited at most twice
Space: O(min(m, n)) where m is charset size
"""
char_index = {}
max_len = 0
left = 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_len = max(max_len, right - left + 1)
return max_len
# lengthOfLongestSubstring("abcabcbb") -> 3 ("abc")
# lengthOfLongestSubstring("bbbbb") -> 1 ("b")
# lengthOfLongestSubstring("pwwkew") -> 3 ("wke")
Problem 14: Product of Array Except Self
LeetCode #238 — Given an integer array nums, return an array answer such that answer[i] is the product of all elements except nums[i]. You must solve it without division and in O(n) time.
Pattern: Prefix/Suffix Products — Build left products, then multiply by right products.
ML Context: Prefix/suffix computations are used in parallel scan algorithms on GPUs, which are foundational to efficient transformer implementations and cumulative statistics in streaming ML systems.
def productExceptSelf(nums):
"""
Time: O(n) - two passes
Space: O(1) extra (output array not counted)
"""
n = len(nums)
result = [1] * n
# Left pass: result[i] = product of all elements to the left
prefix = 1
for i in range(n):
result[i] = prefix
prefix *= nums[i]
# Right pass: multiply by product of all elements to the right
suffix = 1
for i in range(n - 1, -1, -1):
result[i] *= suffix
suffix *= nums[i]
return result
# productExceptSelf([1,2,3,4]) -> [24,12,8,6]
# productExceptSelf([-1,1,0,-3,3]) -> [0,0,9,0,0]
Problem 15: Group Anagrams
LeetCode #49 — Given an array of strings, group the anagrams together. An anagram is a word formed by rearranging the letters of another word.
Pattern: Hash Map with sorted key — Two strings are anagrams if their sorted forms are equal.
ML Context: Grouping by canonical form is used in entity resolution and deduplication in NLP pipelines. Named entity linking groups different surface forms of the same entity.
from collections import defaultdict
def groupAnagrams(strs):
"""
Time: O(n * k log k) where k is max string length
Space: O(n * k) for the hash map
"""
groups = defaultdict(list)
for s in strs:
key = tuple(sorted(s))
groups[key].append(s)
return list(groups.values())
# Alternative O(n*k) using character counts as key:
def groupAnagramsOptimal(strs):
groups = defaultdict(list)
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
groups[tuple(count)].append(s)
return list(groups.values())
# groupAnagrams(["eat","tea","tan","ate","nat","bat"])
# -> [["eat","tea","ate"],["tan","nat"],["bat"]]
Problem 16: Merge Intervals
LeetCode #56 — Given an array of intervals where intervals[i] = [start_i, end_i], merge all overlapping intervals.
Pattern: Sort + Linear Scan — Sort by start time, merge if current overlaps with previous.
ML Context: Interval merging is used in GPU memory management for model training, time-series event deduplication, and scheduling training jobs on shared clusters.
def merge(intervals):
"""
Time: O(n log n) for sorting
Space: O(n) for output
"""
intervals.sort(key=lambda x: x[0])
merged = [intervals[0]]
for start, end in intervals[1:]:
if start <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], end)
else:
merged.append([start, end])
return merged
# merge([[1,3],[2,6],[8,10],[15,18]]) -> [[1,6],[8,10],[15,18]]
# merge([[1,4],[4,5]]) -> [[1,5]]
Problem 17: Sort Colors
LeetCode #75 — Given an array with n objects colored red (0), white (1), or blue (2), sort them in-place so that objects of the same color are adjacent (Dutch National Flag problem).
Pattern: Three-way Partition (Dutch National Flag) — Three pointers: low, mid, high.
ML Context: Three-way partitioning is used in quickselect for finding k-th percentile features and in data stratification for balanced train/validation/test splits.
def sortColors(nums):
"""
Dutch National Flag Algorithm
Time: O(n) - single pass
Space: O(1) - in-place
"""
low, mid, high = 0, 0, len(nums) - 1
while mid <= high:
if nums[mid] == 0:
nums[low], nums[mid] = nums[mid], nums[low]
low += 1
mid += 1
elif nums[mid] == 1:
mid += 1
else:
nums[mid], nums[high] = nums[high], nums[mid]
high -= 1
# nums = [2,0,2,1,1,0]; sortColors(nums) -> [0,0,1,1,2,2]
Problem 18: Subarray Sum Equals K
LeetCode #560 — Given an array of integers and an integer k, return the total number of subarrays whose sum equals k.
Pattern: Prefix Sum + Hash Map — Store prefix sum frequencies; count[prefix - k] gives subarrays ending here with sum k.
ML Context: Prefix sum techniques are used in computing cumulative statistics for streaming data, efficient attention score computation, and integral images for object detection.
from collections import defaultdict
def subarraySum(nums, k):
"""
Time: O(n) - single pass
Space: O(n) - hash map of prefix sums
"""
count = 0
prefix_sum = 0
prefix_counts = defaultdict(int)
prefix_counts[0] = 1 # empty subarray
for num in nums:
prefix_sum += num
count += prefix_counts[prefix_sum - k]
prefix_counts[prefix_sum] += 1
return count
# subarraySum([1,1,1], 2) -> 2
# subarraySum([1,2,3], 3) -> 2 ([1,2] and [3])
Problem 19: Rotate Array
LeetCode #189 — Given an integer array nums, rotate the array to the right by k steps.
Pattern: Reverse Three Times — Reverse all, reverse first k, reverse rest.
ML Context: Array rotation and circular buffer operations appear in ring buffers for experience replay in reinforcement learning and circular data loading patterns in training pipelines.
def rotate(nums, k):
"""
Time: O(n) - three reversals
Space: O(1) - in-place
"""
k = k % len(nums)
def reverse(left, right):
while left < right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
reverse(0, len(nums) - 1) # reverse all
reverse(0, k - 1) # reverse first k
reverse(k, len(nums) - 1) # reverse rest
# nums = [1,2,3,4,5,6,7], k=3 -> [5,6,7,1,2,3,4]
Problem 20: Spiral Matrix
LeetCode #54 — Given an m x n matrix, return all elements in spiral order.
Pattern: Boundary Simulation — Maintain top/bottom/left/right boundaries, shrink after each pass.
ML Context: Matrix traversal patterns are fundamental to understanding how convolution kernels slide over feature maps in CNNs. Spiral and zigzag scans appear in image compression (JPEG) and quantization.
def spiralOrder(matrix):
"""
Time: O(m * n) - visit every element
Space: O(1) extra (excluding output)
"""
result = []
top, bottom = 0, len(matrix) - 1
left, right = 0, len(matrix[0]) - 1
while top <= bottom and left <= right:
for col in range(left, right + 1): # go right
result.append(matrix[top][col])
top += 1
for row in range(top, bottom + 1): # go down
result.append(matrix[row][right])
right -= 1
if top <= bottom:
for col in range(right, left - 1, -1): # go left
result.append(matrix[bottom][col])
bottom -= 1
if left <= right:
for row in range(bottom, top - 1, -1): # go up
result.append(matrix[row][left])
left += 1
return result
# spiralOrder([[1,2,3],[4,5,6],[7,8,9]]) -> [1,2,3,6,9,8,7,4,5]
Key Takeaways
- Two pointers on sorted data (3Sum, Container) reduces O(n^2) to O(n) per fixed element. Always consider sorting first.
- Sliding window (Longest Substring) handles variable-length subarray/substring problems efficiently.
- Prefix sums (Product of Array, Subarray Sum K) precompute cumulative values for O(1) range queries.
- Hash maps for grouping (Group Anagrams) are the standard approach for classification-by-key problems.
- In-place algorithms (Sort Colors, Rotate Array) demonstrate the space optimization skills interviewers value.