Getting Started Problem Set
This is the first stop on the problem-set track: a warm-up mix that touches most of the core patterns from earlier phases without leaning too hard on any single one. If you can clear these eight, you’re ready for the sharper Arrays and Strings and Trees and Graphs sets that follow.
What you’ll learn
Section titled “What you’ll learn”- How to turn a pattern you already know (hash maps, two pointers, Kadane’s algorithm, linked-list rewiring) into working code under light pressure.
- How to read a stub, find the
# TODO, and make the printed output match the# expectcomment. - That “press Run, see it fail, fix one line, press Run again” is a normal and fast loop — not a sign you’re stuck.
How to use this set
Section titled “How to use this set”Each problem below has three parts:
- A problem statement with the constraints that matter.
- A runnable stub — a function with a
# TODOand someprint(...)calls under a# Sample testscomment. Press Run on the code block to execute it right here in the browser. - A collapsed Show solution — a complete, annotated answer with its time/space complexity. Open it only after you’ve genuinely tried.
Problems
Section titled “Problems”1. Two Sum — LC 1 — Easy
Section titled “1. Two Sum — LC 1 — Easy”Pattern: Hash Map — see Hash Tables.
Problem. Given an array of integers nums and an integer target,
return the indices of the two numbers that add up to target. Assume
exactly one valid answer exists, and you may not use the same element
twice. 2 <= len(nums) <= 10^4.
def two_sum(nums, target):
# TODO: return indices of the two numbers adding to target
pass
# Sample tests (press Run):
print(two_sum([2, 7, 11, 15], 9)) # expect [0, 1]
print(two_sum([3, 2, 4], 6)) # expect [1, 2]
print(two_sum([3, 3], 6)) # expect [0, 1]Show solution
def two_sum(nums, target):
seen = {} # value -> index seen so far
for i, n in enumerate(nums):
complement = target - n
if complement in seen:
return [seen[complement], i]
seen[n] = i
return []
print(two_sum([2, 7, 11, 15], 9)) # [0, 1]
print(two_sum([3, 2, 4], 6)) # [1, 2]
print(two_sum([3, 3], 6)) # [0, 1]One pass, one hash map: for every number, check whether its complement was already seen before inserting the current number. Time: . Space: .
2. Valid Palindrome — LC 125 — Easy
Section titled “2. Valid Palindrome — LC 125 — Easy”Pattern: Two Pointers — see Two Pointers.
Problem. Given a string s, return True if it reads the same
forwards and backwards after converting to lowercase and removing every
character that isn’t alphanumeric.
def is_palindrome(s):
# TODO: return True if s is a palindrome, ignoring case and non-alphanumerics
pass
# Sample tests (press Run):
print(is_palindrome("A man, a plan, a canal: Panama")) # expect True
print(is_palindrome("race a car")) # expect False
print(is_palindrome(" ")) # expect TrueShow solution
def is_palindrome(s):
cleaned = [c.lower() for c in s if c.isalnum()]
left, right = 0, len(cleaned) - 1
while left < right:
if cleaned[left] != cleaned[right]:
return False
left += 1
right -= 1
return True
print(is_palindrome("A man, a plan, a canal: Panama")) # True
print(is_palindrome("race a car")) # False
print(is_palindrome(" ")) # TrueClean once, then close in from both ends — the moment two characters
disagree, it can’t be a palindrome. Time: . Space:
for the cleaned copy (can be made by indexing into s directly and
skipping non-alphanumeric characters as you go).
3. Best Time to Buy and Sell Stock — LC 121 — Easy
Section titled “3. Best Time to Buy and Sell Stock — LC 121 — Easy”Pattern: One-pass / greedy scan (same-direction pointer) — see Two Pointers.
Problem. Given an array prices where prices[i] is the price of a
stock on day i, choose a single day to buy and a later day to sell to
maximize profit. Return the maximum profit, or 0 if no profit is
possible.
def max_profit(prices):
# TODO: return the max profit from one buy and one later sell
pass
# Sample tests (press Run):
print(max_profit([7, 1, 5, 3, 6, 4])) # expect 5
print(max_profit([7, 6, 4, 3, 1])) # expect 0Show solution
def max_profit(prices):
min_price = float("inf")
best = 0
for price in prices:
min_price = min(min_price, price)
best = max(best, price - min_price)
return best
print(max_profit([7, 1, 5, 3, 6, 4])) # 5
print(max_profit([7, 6, 4, 3, 1])) # 0Track the lowest price seen so far as you scan left to right; at every day, ask “what if I sold today?” and keep the best of those answers. Time: . Space: .
4. Contains Duplicate — LC 217 — Easy
Section titled “4. Contains Duplicate — LC 217 — Easy”Pattern: Hash Set — see Hash Tables.
Problem. Given an integer array nums, return True if any value
appears at least twice, and False if every element is distinct.
def contains_duplicate(nums):
# TODO: return True if any value appears more than once
pass
# Sample tests (press Run):
print(contains_duplicate([1, 2, 3, 1])) # expect True
print(contains_duplicate([1, 2, 3, 4])) # expect FalseShow solution
def contains_duplicate(nums):
seen = set()
for n in nums:
if n in seen:
return True
seen.add(n)
return False
print(contains_duplicate([1, 2, 3, 1])) # True
print(contains_duplicate([1, 2, 3, 4])) # FalseA set gives average membership checks, so one linear pass is enough. Time: . Space: .
5. Valid Anagram — LC 242 — Easy
Section titled “5. Valid Anagram — LC 242 — Easy”Pattern: Hash Map (character counting) — see Hash Tables.
Problem. Given two strings s and t, return True if t is an
anagram of s (same characters, same multiplicity, any order).
def is_anagram(s, t):
# TODO: return True if t is an anagram of s
pass
# Sample tests (press Run):
print(is_anagram("anagram", "nagaram")) # expect True
print(is_anagram("rat", "car")) # expect FalseShow solution
from collections import Counter
def is_anagram(s, t):
if len(s) != len(t):
return False
return Counter(s) == Counter(t)
print(is_anagram("anagram", "nagaram")) # True
print(is_anagram("rat", "car")) # FalseTwo strings are anagrams exactly when their character-frequency maps are
equal — Counter builds that map for you. Time: . Space:
(bounded alphabet) or for a general Unicode alphabet.
6. Maximum Subarray (Kadane) — LC 53 — Medium
Section titled “6. Maximum Subarray (Kadane) — LC 53 — Medium”Pattern: 1-D Dynamic Programming — see One Dimensional DP.
Problem. Given an integer array nums, find the contiguous subarray
with the largest sum and return that sum. The array has at least one
element.
def max_subarray(nums):
# TODO: return the largest sum of any contiguous subarray
pass
# Sample tests (press Run):
print(max_subarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])) # expect 6
print(max_subarray([1])) # expect 1
print(max_subarray([5, 4, -1, 7, 8])) # expect 23Show solution
def max_subarray(nums):
best = nums[0]
current = nums[0]
for n in nums[1:]:
current = max(n, current + n) # extend the run, or start fresh at n
best = max(best, current)
return best
print(max_subarray([-2, 1, -3, 4, -1, 2, 1, -5, 4])) # 6
print(max_subarray([1])) # 1
print(max_subarray([5, 4, -1, 7, 8])) # 23Kadane’s algorithm is a 1-D DP in disguise: current is “best sum of a
subarray ending exactly here.” At each step it’s either worth extending or
worth abandoning and restarting at the current element. Time: .
Space: .
7. Fizz Buzz — LC 412 — Easy
Section titled “7. Fizz Buzz — LC 412 — Easy”Pattern: Simulation / modulo arithmetic (no dedicated pattern page — this one is about clean conditionals, not a named technique).
Problem. Given an integer n, return a list of strings for the
numbers 1 to n where: multiples of 3 become "Fizz", multiples of 5
become "Buzz", multiples of both become "FizzBuzz", and everything
else is the number itself as a string.
def fizz_buzz(n):
# TODO: return list of strings 1..n applying the Fizz/Buzz/FizzBuzz rules
pass
# Sample tests (press Run):
print(fizz_buzz(5)) # expect ['1', '2', 'Fizz', '4', 'Buzz']
print(repr(fizz_buzz(15)[-1])) # expect 'FizzBuzz'Show solution
def fizz_buzz(n):
result = []
for i in range(1, n + 1):
if i % 15 == 0:
result.append("FizzBuzz")
elif i % 3 == 0:
result.append("Fizz")
elif i % 5 == 0:
result.append("Buzz")
else:
result.append(str(i))
return result
print(fizz_buzz(5)) # ['1', '2', 'Fizz', '4', 'Buzz']
print(repr(fizz_buzz(15)[-1])) # 'FizzBuzz'Check the 15 case (both 3 and 5) before the individual cases, or "Fizz"
and "Buzz" will each fire on their own and you’ll never print
"FizzBuzz". Time: . Space: for the output list.
8. Reverse Linked List — LC 206 — Easy
Section titled “8. Reverse Linked List — LC 206 — Easy”Pattern: In-place pointer rewiring — see In-place Linked List Reversal.
Problem. Given the head of a singly linked list, reverse the list and return the new head.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def build_list(values):
head = None
for v in reversed(values):
head = ListNode(v, head)
return head
def to_list(head):
values = []
while head:
values.append(head.val)
head = head.next
return values
def reverse_list(head):
# TODO: reverse the linked list in place and return the new head
pass
# Sample tests (press Run):
print(to_list(reverse_list(build_list([1, 2, 3, 4, 5])))) # expect [5, 4, 3, 2, 1]
print(to_list(reverse_list(build_list([])))) # expect []Show solution
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def build_list(values):
head = None
for v in reversed(values):
head = ListNode(v, head)
return head
def to_list(head):
values = []
while head:
values.append(head.val)
head = head.next
return values
def reverse_list(head):
prev = None
current = head
while current:
nxt = current.next # save the rest of the list before overwriting .next
current.next = prev # point backwards
prev = current
current = nxt
return prev
print(to_list(reverse_list(build_list([1, 2, 3, 4, 5])))) # [5, 4, 3, 2, 1]
print(to_list(reverse_list(build_list([])))) # []Three pointers (prev, current, nxt) walk the list once, flipping one
.next link per step. No extra list or recursion needed. Time:
. Space: .
Complexity
Section titled “Complexity”Every problem on this page has an intended bound, and in each case the naive answer is one complexity class worse. Knowing which class the constraint is asking for is most of the skill.
| # | Problem | Naive | Intended | What buys the improvement |
|---|---|---|---|---|
| 1 | Two Sum | nested loops | time, space | A hash map turns “is the complement present?” from a scan into a lookup |
| 2 | Valid Palindrome | time, space (build a cleaned copy) | time, space | Two pointers skipping non-alphanumerics in place |
| 3 | Best Time to Buy and Sell | over every pair | , | Track the minimum seen so far; the best sell today only needs that one number |
| 4 | Contains Duplicate | pairwise, or by sorting | , | len(set(nums)) != len(nums) |
| 5 | Valid Anagram | sort both | , for a fixed alphabet | A Counter, compared once |
| 6 | Maximum Subarray | over all subarrays | , | Kadane: at each step, extend or restart |
| 7 | Fizz Buzz | — | Nothing to optimise — it is a clarity exercise | |
| 8 | Reverse Linked List | time, space (copy to a list) | time, space | Three pointers, rewiring in place |
Three habits worth forming here, because they carry all the way to the hard problems:
- “Sorting first” costs and you often do not need it. Contains Duplicate and Valid Anagram both have hash-based answers. Sorting is a fine first answer to state; it should not be your final one when a counting structure exists.
- Space is a separate axis from time. Problems 2 and 8 are time either way — the whole question is whether you use or extra space. Interviewers ask “can you do it in constant space?” precisely on these.
- for a fixed alphabet is worth saying out loud. A 26-entry counter is technically bounded, so Valid Anagram is space; a general-Unicode version is in the number of distinct characters. Naming which model you are in is more precise than either bare answer.
Pitfalls
Section titled “Pitfalls”The failures below account for most first-attempt misses on this page. Each is a specific wrong line, not general advice.
- Two Sum: inserting into the map before checking it. Do the complement lookup first. Insert
first and
[3, 3]with target 6 returns[0, 0]— the same index twice, which is not a valid pair. Verified: correct order gives[0, 1], the wrong order gives[0, 0]. - Two Sum: returning values instead of indices. LC 1 wants indices; LC 167 wants 1-based
indices. Read the return spec, and note LC 167’s
+1. - Valid Palindrome: filtering into a new string, then reversing. Correct, but it is space when the two-pointer version is . If the interviewer asks for constant space you have to rewrite — so write the pointer version.
- Best Time to Buy and Sell: allowing a sell before the buy. Track
min_so_farand computeprice - min_so_farin the same pass. Comparing against the global minimum (which may occur later) yields a profit you could not actually have made. - Best Time to Buy and Sell: initialising
best = 0versusbest = -inf. LC 121 permits doing nothing, so0is right. LC 53 (Maximum Subarray) does not permit an empty selection, so0is wrong there — see the next bullet. - Kadane with a zero floor.
cur = max(0, cur + v)returns 0 on an all-negative array;cur = max(v, cur + v)returns -1 for[-3, -1, -2], which is the correct answer. Verified both ways. The zero-floor version happens to agree on every array containing a positive number, which is why it passes casual testing. - Valid Anagram: comparing sorted strings and calling it . It is . The
Counterversion is the one. - Contains Duplicate: sorting to find adjacent equals. Works, , and mutates the input. The set version is one line and .
- Reverse Linked List: overwriting
head.nextbefore saving it. Savenxt = head.nextfirst, or the rest of the list is unreachable and you are left with a two-node loop. - Reverse Linked List: returning
headinstead ofprev. After the loopheadisNone;previs the new front. Returningheadgives an empty list, and the empty-input case ([]->[]) hides the bug because both areNone. - Not running the sample tests before moving on. The stubs print
# expectvalues for a reason — press Run. “It looks right” is not a test, and the whole point of this page is the fast fail-fix-rerun loop.
Drills
Section titled “Drills”The stubs above are whole problems. These three are micro-drills on the exact lines people get wrong — each one contrasts the correct version with the plausible wrong one, so you can see the difference in the output rather than take it on trust.
Drill 1 — the order of lookup and insert
Section titled “Drill 1 — the order of lookup and insert”Drill 2 — Kadane’s reset, and the zero-floor trap
Section titled “Drill 2 — Kadane’s reset, and the zero-floor trap”Drill 3 — rewiring a linked list without losing the tail
Section titled “Drill 3 — rewiring a linked list without losing the tail”Interview follow-ups
Section titled “Interview follow-ups”Easy problems get follow-ups too, and they are where the round actually gets decided.
| They ask | On which problem | The answer |
|---|---|---|
| “Can you do it without the hash map?” | Two Sum | If the array were sorted, two pointers at space — but sorting destroys the indices the problem returns, so it costs plus index bookkeeping. With indices required, the hash map is the right trade |
| “What if there are multiple valid pairs?” | Two Sum | LC 1 guarantees exactly one. Without that guarantee, either return the first found or collect all — ask which, because the answer changes the return type |
| “Now do it in constant space” | Valid Palindrome | Two pointers moving inward, skipping non-alphanumerics with str.isalnum(), comparing lower(). No cleaned copy |
| “What counts as a character?” | Valid Palindrome / Valid Anagram | Worth clarifying: case sensitivity, whether to skip punctuation, and whether the input is ASCII or Unicode. The last one decides whether a 26-slot array is enough |
| “What if you could do multiple transactions?” | Best Time to Buy and Sell | LC 122: sum every positive daily difference, still . With a cooldown or a transaction limit it becomes DP — LC 309, 123 |
| “What if the array is all negative?” | Maximum Subarray | Return the largest element. This is exactly why cur = max(v, cur + v) beats a zero floor — the floor returns 0, which is not a valid non-empty subarray sum |
| “Return the subarray, not just the sum” | Maximum Subarray | Track the start index when you restart, and record (start, end) whenever best improves. Same pass, same bound |
| “Reverse it recursively” | Reverse Linked List | Doable in time but stack space, and CPython dies around 1000 frames — so the iterative version is strictly better here. Say that rather than just offering the recursion |
“Reverse only the middle k nodes” | Reverse Linked List | Same three-pointer core plus a dummy head so the join-back has no special case — LC 92, 25. The dummy-head trick is what removes the “what if it starts at the head” branch |
| “How would you test this?” | any | Categories, not examples: empty, one element, all equal, all negative, and the case the algorithm pivots on — the duplicate for Two Sum, the all-negative array for Kadane, the two-element list for the reversal |
| “What is the space complexity? Be exact.” | Valid Anagram | if you commit to a fixed alphabet (26 letters), in the number of distinct characters for general Unicode. State which model you are assuming — that precision is the point of the question |
Practice
Section titled “Practice”Every problem on this page, generated from the problem database — so each row carries its sheet membership and reported companies, and the checkboxes remember what you have finished. The walkthroughs above are the teaching; this is the tracker.
Work down the ladder. Tick each problem off as you go — progress is saved in this browser, and the Export button in the filter bar writes it to a file you can keep.
- 1Two Sumeasy
- 121Best Time to Buy and Sell Stockeasy
- 206Reverse Linked Listeasy
- 242Valid Anagrameasy
- 217Contains Duplicateeasy
- 125Valid Palindromeeasy
- 53Maximum Subarraymedium
- 412Fizz Buzzmedium
Self-check
Section titled “Self-check”-
In hash-map Two Sum, why must you check for the complement before inserting the current value?
Verified: correct order gives [0, 1] and the wrong order gives [0, 0]. It is not specific to duplicate values either -- inserting first means every element is matched against itself the instant it lands in the map, so [3, 2, 4] with target 6 also returns [0, 0] instead of [1, 2].
pch.quizShowAnswer
B — Otherwise an element can match itself: [3, 3] with target 6 returns [0, 0], the same index twice — Verified: correct order gives [0, 1] and the wrong order gives [0, 0]. It is not specific to duplicate values either -- inserting first means every element is matched against itself the instant it lands in the map, so [3, 2, 4] with target 6 also returns [0, 0] instead of [1, 2].
-
Kadane's with `cur = max(0, cur + v)` instead of `cur = max(v, cur + v)`. On which inputs do they differ?
Verified on three inputs: the two versions agree on [-2,1,-3,4,-1,2,1,-5,4] (both 6) and [5] (both 5), and differ only on the all-negative case, 0 against -1. The zero floor silently permits the empty subarray, which LC 53 forbids -- and because it is correct whenever a positive element exists, casual testing never catches it.
pch.quizShowAnswer
B — Only on arrays with no positive element -- [-3, -1, -2] gives 0 instead of the correct -1 — Verified on three inputs: the two versions agree on [-2,1,-3,4,-1,2,1,-5,4] (both 6) and [5] (both 5), and differ only on the all-negative case, 0 against -1. The zero floor silently permits the empty subarray, which LC 53 forbids -- and because it is correct whenever a positive element exists, casual testing never catches it.
-
Contains Duplicate: you sort the array and scan for adjacent equals. What is the interviewer likely to point out?
The sort is correct, just a class slower, and it has a side effect the caller may not expect. "Sort first" is a fine baseline to *state* -- it is a poor place to stop when a counting structure gives O(n). The same applies to Valid Anagram: sorting both strings is O(n log n), a Counter is O(n).
pch.quizShowAnswer
B — It is O(n log n) and mutates the input, while `len(set(nums)) != len(nums)` is O(n) — The sort is correct, just a class slower, and it has a side effect the caller may not expect. "Sort first" is a fine baseline to *state* -- it is a poor place to stop when a counting structure gives O(n). The same applies to Valid Anagram: sorting both strings is O(n log n), a Counter is O(n).
-
Best Time to Buy and Sell Stock: what does tracking `min_so_far` in the same pass guarantee?
Ordering is the whole constraint. Comparing each price against the *global* minimum is wrong when that minimum occurs later -- it reports a profit you could not have made. Computing `price - min_so_far` as you go means the minimum is always from an earlier day. (Non-negativity comes separately, from initialising best to 0, which LC 121 allows because doing nothing is permitted.)
pch.quizShowAnswer
B — That the buy always happens before the sell, since min_so_far only ever looks backwards — Ordering is the whole constraint. Comparing each price against the *global* minimum is wrong when that minimum occurs later -- it reports a profit you could not have made. Computing `price - min_so_far` as you go means the minimum is always from an earlier day. (Non-negativity comes separately, from initialising best to 0, which LC 121 allows because doing nothing is permitted.)
-
Reverse Linked List: what breaks if you write `head.next = prev` before saving `head.next`?
That `next` pointer is the sole reference to the remainder of the list. Overwrite it and everything after the current node is lost -- you end up with a two-node structure. Hence the three-pointer dance: save nxt, rewire, advance both prev and head.
pch.quizShowAnswer
B — The rest of the list becomes unreachable, since the only pointer to it was just overwritten — That `next` pointer is the sole reference to the remainder of the list. Overwrite it and everything after the current node is lost -- you end up with a two-node structure. Hence the three-pointer dance: save nxt, rewire, advance both prev and head.
-
Your `reverse` returns `head` instead of `prev`. Which test case would NOT catch it?
After the loop `head` is None and `prev` is the new front, so returning head gives [] for *every* input. The empty case is the one where [] is the correct answer, so it passes and gives false confidence. A single-element list does catch it -- [] instead of [7]. This is why the drill tests all three.
pch.quizShowAnswer
B — The empty list -- both give None, so it passes — After the loop `head` is None and `prev` is the new front, so returning head gives [] for *every* input. The empty case is the one where [] is the correct answer, so it passes and gives false confidence. A single-element list does catch it -- [] instead of [7]. This is why the drill tests all three.
-
What is the space complexity of the Counter-based Valid Anagram?
Both bare answers are defensible and neither is precise on its own. LC 242 states lowercase English letters, so a 26-slot counter is bounded and O(1) is right. Change the constraint to Unicode and the counter grows with the distinct-character count. Naming the model you are assuming is what the question is actually testing.
pch.quizShowAnswer
B — O(1) if you commit to a fixed alphabet, or O(k) in the number of distinct characters for general Unicode -- say which model you assume — Both bare answers are defensible and neither is precise on its own. LC 242 states lowercase English letters, so a 26-slot counter is bounded and O(1) is right. Change the constraint to Unicode and the counter grows with the distinct-character count. Naming the model you are assuming is what the question is actually testing.
- Eight easy-to-medium problems, one of each core flavor: hash maps, two pointers, Kadane’s DP, plain simulation, and linked-list rewiring.
- The loop is always the same: read the stub, find the
# TODO, press Run, compare the printed output to the# expectcomment, open Show solution only if you’re stuck. - Once your output matches locally, paste the solution into the real problem on LeetCode — this page’s judge is stdlib-only and doesn’t replace the hidden test suite there.
Next: Arrays and Strings Problem Set — the same loop, with harder array/string problems that escalate from sorted-array two pointers all the way to Minimum Window Substring.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading