Easy Problems (1-10)
These ten problems are the foundation. Every AI engineer should be able to solve them cleanly in under 15 minutes each. They cover hash maps, stacks, two pointers, linked lists, trees, and dynamic programming basics — patterns that appear in every interview.
Problem 1: Two Sum
LeetCode #1 — Given an array of integers nums and an integer target, return indices of the two numbers that add up to target. You may assume each input has exactly one solution and you may not use the same element twice.
Pattern: Hash Map — Use a dictionary to store complements for O(1) lookup.
ML Context: Hash-based lookups power feature stores in recommendation systems. When you retrieve user features during inference, you need O(1) access, exactly like this problem.
def twoSum(nums, target):
"""
Time: O(n) - single pass through array
Space: O(n) - hash map stores up to n elements
"""
seen = {} # value -> index
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
# Example:
# twoSum([2, 7, 11, 15], 9) -> [0, 1]
# twoSum([3, 2, 4], 6) -> [1, 2]
Problem 2: Valid Parentheses
LeetCode #20 — Given a string containing just the characters (, ), {, }, [, ], determine if the input string is valid. An input string is valid if open brackets are closed by the same type and in the correct order.
Pattern: Stack — Push opening brackets, pop and match on closing brackets.
ML Context: Compilers and parsers for ML frameworks (like ONNX model definitions or TensorFlow graph serialization) use stack-based matching to validate nested structures.
def isValid(s):
"""
Time: O(n) - single pass through string
Space: O(n) - stack stores up to n/2 brackets
"""
stack = []
mapping = {')': '(', '}': '{', ']': '['}
for char in s:
if char in mapping:
if not stack or stack[-1] != mapping[char]:
return False
stack.pop()
else:
stack.append(char)
return len(stack) == 0
# isValid("()[]{}") -> True
# isValid("(]") -> False
# isValid("([)]") -> False
Problem 3: Merge Two Sorted Lists
LeetCode #21 — Merge two sorted linked lists and return it as a sorted list. The list should be made by splicing together the nodes of the first two lists.
Pattern: Two Pointers — Compare heads of both lists, advance the smaller one.
ML Context: Merging sorted sequences is fundamental to merge sort, which is used in external sorting for large datasets that do not fit in memory — common in data preprocessing pipelines.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def mergeTwoLists(list1, list2):
"""
Time: O(n + m) where n, m are list lengths
Space: O(1) - we reuse existing nodes
"""
dummy = ListNode(0)
current = dummy
while list1 and list2:
if list1.val <= list2.val:
current.next = list1
list1 = list1.next
else:
current.next = list2
list2 = list2.next
current = current.next
current.next = list1 or list2
return dummy.next
Problem 4: Best Time to Buy and Sell Stock
LeetCode #121 — Given an array prices where prices[i] is the price of a stock on day i, find the maximum profit from one buy and one sell. You must buy before you sell.
Pattern: Track minimum so far — At each price, compute profit if selling today, update minimum.
ML Context: This min-tracking pattern appears in training loss monitoring. You track the best validation loss seen so far (early stopping) using the exact same logic.
def maxProfit(prices):
"""
Time: O(n) - single pass
Space: O(1) - two variables
"""
min_price = float('inf')
max_profit = 0
for price in prices:
min_price = min(min_price, price)
max_profit = max(max_profit, price - min_price)
return max_profit
# maxProfit([7,1,5,3,6,4]) -> 5 (buy at 1, sell at 6)
# maxProfit([7,6,4,3,1]) -> 0 (no profitable transaction)
Problem 5: Valid Palindrome
LeetCode #125 — Given a string s, return true if it is a palindrome after converting to lowercase and removing non-alphanumeric characters.
Pattern: Two Pointers — Compare characters from both ends, skip non-alphanumeric.
ML Context: Text preprocessing is fundamental to NLP pipelines. Normalizing and cleaning strings before tokenization uses similar character-level logic.
def isPalindrome(s):
"""
Time: O(n) - two pointers meet in the middle
Space: O(1) - in-place comparison
"""
left, right = 0, len(s) - 1
while left < right:
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return True
# isPalindrome("A man, a plan, a canal: Panama") -> True
# isPalindrome("race a car") -> False
Problem 6: Linked List Cycle
LeetCode #141 — Given head, the head of a linked list, determine if the linked list has a cycle in it.
Pattern: Fast/Slow Pointers (Floyd's Cycle Detection) — Two pointers at different speeds; if they meet, there is a cycle.
ML Context: Cycle detection in computational graphs prevents infinite loops during backpropagation. PyTorch and TensorFlow both need to detect cycles when building autograd graphs.
def hasCycle(head):
"""
Time: O(n) - fast pointer traverses at most 2n
Space: O(1) - two pointers only
"""
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
Problem 7: Climbing Stairs
LeetCode #70 — You are climbing a staircase with n steps. Each time you can climb 1 or 2 steps. How many distinct ways can you reach the top?
Pattern: Dynamic Programming — dp[i] = dp[i-1] + dp[i-2]. This is Fibonacci with different base cases.
ML Context: This recurrence structure appears in sequence modeling. The Viterbi algorithm for HMMs uses similar DP to find the most likely sequence of hidden states.
def climbStairs(n):
"""
Time: O(n) - single pass
Space: O(1) - two variables
"""
if n <= 2:
return n
prev2, prev1 = 1, 2
for i in range(3, n + 1):
curr = prev1 + prev2
prev2 = prev1
prev1 = curr
return prev1
# climbStairs(2) -> 2 (1+1 or 2)
# climbStairs(3) -> 3 (1+1+1, 1+2, 2+1)
# climbStairs(10) -> 89
Problem 8: Binary Tree Inorder Traversal
LeetCode #94 — Given the root of a binary tree, return the inorder traversal of its nodes' values (left, root, right).
Pattern: Tree Traversal — Recursive or iterative with a stack.
ML Context: Decision trees and random forests traverse tree structures during both training and inference. Understanding tree traversal orders is essential for implementing these models from scratch.
def inorderTraversal(root):
"""
Iterative approach using explicit stack.
Time: O(n) - visit every node
Space: O(h) - stack depth equals tree height
"""
result = []
stack = []
current = root
while current or stack:
while current:
stack.append(current)
current = current.left
current = stack.pop()
result.append(current.val)
current = current.right
return result
# Recursive alternative (simpler but uses call stack):
def inorderRecursive(root):
if not root:
return []
return inorderRecursive(root.left) + [root.val] + inorderRecursive(root.right)
Problem 9: Maximum Subarray
LeetCode #53 — Given an integer array nums, find the subarray with the largest sum and return its sum.
Pattern: Kadane's Algorithm — Track current sum and maximum sum. Reset current sum when it drops below zero.
ML Context: Kadane's algorithm is used in time-series anomaly detection to find the period with the largest cumulative deviation. It also appears in feature importance analysis on sequential data.
def maxSubArray(nums):
"""
Kadane's Algorithm
Time: O(n) - single pass
Space: O(1) - two variables
"""
max_sum = nums[0]
current_sum = nums[0]
for i in range(1, len(nums)):
current_sum = max(nums[i], current_sum + nums[i])
max_sum = max(max_sum, current_sum)
return max_sum
# maxSubArray([-2,1,-3,4,-1,2,1,-5,4]) -> 6 (subarray [4,-1,2,1])
# maxSubArray([1]) -> 1
# maxSubArray([-1]) -> -1
Problem 10: Contains Duplicate
LeetCode #217 — Given an integer array nums, return true if any value appears at least twice.
Pattern: Hash Set — Add each element to a set; if it already exists, return True.
ML Context: Deduplication is critical in ML data pipelines. Training on duplicate data causes the model to overfit to repeated examples. Hash-based deduplication is the standard approach in dataset curation.
def containsDuplicate(nums):
"""
Time: O(n) - single pass
Space: O(n) - set stores up to n elements
"""
seen = set()
for num in nums:
if num in seen:
return True
seen.add(num)
return False
# Alternative one-liner:
# def containsDuplicate(nums): return len(nums) != len(set(nums))
# containsDuplicate([1,2,3,1]) -> True
# containsDuplicate([1,2,3,4]) -> False
Key Takeaways
- Hash maps and sets are the most powerful tools for reducing O(n^2) to O(n). Problems 1, 10, and many medium problems rely on this pattern.
- Two pointers (Problems 3, 5, 6) work whenever the data is sorted or you need to compare elements from different positions.
- Kadane's algorithm (Problem 9) is the simplest form of DP and a building block for harder DP problems.
- Stack (Problem 2) is the go-to for matching nested structures. You will see it again in harder problems like Trapping Rain Water.
- Every easy problem has a direct connection to real AI/ML systems. Understanding these patterns makes you a better ML engineer, not just a better interviewee.
Lilly Tech Systems