Skip to content

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.

  • 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 # expect comment.
  • That “press Run, see it fail, fix one line, press Run again” is a normal and fast loop — not a sign you’re stuck.

Each problem below has three parts:

  1. A problem statement with the constraints that matter.
  2. A runnable stub — a function with a # TODO and some print(...) calls under a # Sample tests comment. Press Run on the code block to execute it right here in the browser.
  3. A collapsed Show solution — a complete, annotated answer with its time/space complexity. Open it only after you’ve genuinely tried.

Open LC 1 on LeetCode

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.

two_sum.py
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
python
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: O(n)O(n). Space: O(n)O(n).

Open LC 125 on LeetCode

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.

valid_palindrome.py
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 True
Show solution
python
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(" "))                                  # True

Clean once, then close in from both ends — the moment two characters disagree, it can’t be a palindrome. Time: O(n)O(n). Space: O(n)O(n) for the cleaned copy (can be made O(1)O(1) 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”

Open LC 121 on LeetCode

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.

max_profit.py
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 0
Show solution
python
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]))      # 0

Track 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: O(n)O(n). Space: O(1)O(1).

Open LC 217 on LeetCode

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.

contains_duplicate.py
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 False
Show solution
python
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]))   # False

A set gives O(1)O(1) average membership checks, so one linear pass is enough. Time: O(n)O(n). Space: O(n)O(n).

Open LC 242 on LeetCode

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).

valid_anagram.py
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 False
Show solution
python
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"))           # False

Two strings are anagrams exactly when their character-frequency maps are equal — Counter builds that map for you. Time: O(n)O(n). Space: O(1)O(1) (bounded alphabet) or O(n)O(n) for a general Unicode alphabet.

6. Maximum Subarray (Kadane) — LC 53 — Medium

Section titled “6. Maximum Subarray (Kadane) — LC 53 — Medium”

Open LC 53 on LeetCode

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.

max_subarray.py
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 23
Show solution
python
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]))                   # 23

Kadane’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: O(n)O(n). Space: O(1)O(1).

Open LC 412 on LeetCode

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.

fizz_buzz.py
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
python
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: O(n)O(n). Space: O(n)O(n) for the output list.

8. Reverse Linked List — LC 206 — Easy

Section titled “8. Reverse Linked List — LC 206 — Easy”

Open LC 206 on LeetCode

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.

reverse_linked_list.py
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
python
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: O(n)O(n). Space: O(1)O(1).

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.

#ProblemNaiveIntendedWhat buys the improvement
1Two SumO(n2)O(n^2) nested loopsO(n)O(n) time, O(n)O(n) spaceA hash map turns “is the complement present?” from a scan into a lookup
2Valid PalindromeO(n)O(n) time, O(n)O(n) space (build a cleaned copy)O(n)O(n) time, O(1)O(1) spaceTwo pointers skipping non-alphanumerics in place
3Best Time to Buy and SellO(n2)O(n^2) over every pairO(n)O(n), O(1)O(1)Track the minimum seen so far; the best sell today only needs that one number
4Contains DuplicateO(n2)O(n^2) pairwise, or O(nlogn)O(n \log n) by sortingO(n)O(n), O(n)O(n)len(set(nums)) != len(nums)
5Valid AnagramO(nlogn)O(n \log n) sort bothO(n)O(n), O(1)O(1) for a fixed alphabetA Counter, compared once
6Maximum SubarrayO(n2)O(n^2) over all subarraysO(n)O(n), O(1)O(1)Kadane: at each step, extend or restart
7Fizz BuzzO(n)O(n)Nothing to optimise — it is a clarity exercise
8Reverse Linked ListO(n)O(n) time, O(n)O(n) space (copy to a list)O(n)O(n) time, O(1)O(1) spaceThree pointers, rewiring in place

Three habits worth forming here, because they carry all the way to the hard problems:

  • “Sorting first” costs O(nlogn)O(n \log n) and you often do not need it. Contains Duplicate and Valid Anagram both have O(n)O(n) 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 O(n)O(n) time either way — the whole question is whether you use O(n)O(n) or O(1)O(1) extra space. Interviewers ask “can you do it in constant space?” precisely on these.
  • O(1)O(1) for a fixed alphabet is worth saying out loud. A 26-entry counter is technically bounded, so Valid Anagram is O(1)O(1) space; a general-Unicode version is O(k)O(k) in the number of distinct characters. Naming which model you are in is more precise than either bare answer.

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 O(n)O(n) space when the two-pointer version is O(1)O(1). 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_far and compute price - min_so_far in 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 = 0 versus best = -inf. LC 121 permits doing nothing, so 0 is right. LC 53 (Maximum Subarray) does not permit an empty selection, so 0 is 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 O(n)O(n). It is O(nlogn)O(n \log n). The Counter version is the O(n)O(n) one.
  • Contains Duplicate: sorting to find adjacent equals. Works, O(nlogn)O(n \log n), and mutates the input. The set version is one line and O(n)O(n).
  • Reverse Linked List: overwriting head.next before saving it. Save nxt = head.next first, or the rest of the list is unreachable and you are left with a two-node loop.
  • Reverse Linked List: returning head instead of prev. After the loop head is None; prev is the new front. Returning head gives an empty list, and the empty-input case ([] -> []) hides the bug because both are None.
  • Not running the sample tests before moving on. The stubs print # expect values 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.

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”

Easy problems get follow-ups too, and they are where the round actually gets decided.

They askOn which problemThe answer
“Can you do it without the hash map?”Two SumIf the array were sorted, two pointers at O(1)O(1) space — but sorting destroys the indices the problem returns, so it costs O(nlogn)O(n \log n) plus index bookkeeping. With indices required, the hash map is the right trade
“What if there are multiple valid pairs?”Two SumLC 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 PalindromeTwo pointers moving inward, skipping non-alphanumerics with str.isalnum(), comparing lower(). No cleaned copy
“What counts as a character?”Valid Palindrome / Valid AnagramWorth 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 SellLC 122: sum every positive daily difference, still O(n)O(n). With a cooldown or a transaction limit it becomes DP — LC 309, 123
“What if the array is all negative?”Maximum SubarrayReturn 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 SubarrayTrack the start index when you restart, and record (start, end) whenever best improves. Same pass, same bound
“Reverse it recursively”Reverse Linked ListDoable in O(n)O(n) time but O(n)O(n) 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 ListSame 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?”anyCategories, 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 AnagramO(1)O(1) if you commit to a fixed alphabet (26 letters), O(k)O(k) in the number of distinct characters for general Unicode. State which model you are assuming — that precision is the point of the question

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.

8 problems
6 easy2 medium0 hard

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 SumeasyNeetCode 150Blind 75LeetCode Top Interview 150amazongooglemetamicrosoftapplebloomberg
  • 121Best Time to Buy and Sell StockeasyNeetCode 150Blind 75LeetCode Top Interview 150amazonmetamicrosoftbloomberg
  • 206Reverse Linked ListeasyNeetCode 150Blind 75amazonmicrosoftapplebloombergmeta
  • 242Valid AnagrameasyNeetCode 150Blind 75LeetCode Top Interview 150amazonmetabloomberg
  • 217Contains DuplicateeasyNeetCode 150Blind 75amazonapplemicrosoft
  • 125Valid PalindromeeasyNeetCode 150Blind 75LeetCode Top Interview 150
  • 53Maximum SubarraymediumNeetCode 150Blind 75LeetCode Top Interview 150amazonmicrosoftbloombergapple
  • 412Fizz Buzzmedium
pch.quizTag pch.quizDefaultTitle
  1. In hash-map Two Sum, why must you check for the complement before inserting the current value?

    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].

  2. Kadane's with `cur = max(0, cur + v)` instead of `cur = max(v, cur + v)`. On which inputs do they differ?

    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.

  3. Contains Duplicate: you sort the array and scan for adjacent equals. What is the interviewer likely to point out?

    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).

  4. Best Time to Buy and Sell Stock: what does tracking `min_so_far` in the same pass guarantee?

    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.)

  5. Reverse Linked List: what breaks if you write `head.next = prev` before saving `head.next`?

    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.

  6. Your `reverse` returns `head` instead of `prev`. Which test case would NOT catch it?

    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.

  7. What is the space complexity of the Counter-based Valid Anagram?

    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 # expect comment, 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.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading