10 LeetCode Warm-Up Problems You Can Solve in Under 15 Minutes

June 7, 202612 min read
dsaalgorithmsinterview-prepleetcode
10 LeetCode Warm-Up Problems You Can Solve in Under 15 Minutes
TL;DR
  • Hash set membership (Contains Duplicate) is the O(1) reflex behind anagram checks, cycle detection, and any duplicate-existence problem
  • Complement lookup (Two Sum) stores what you've already seen and checks backward, covering every find-a-pair problem
  • Kadane's algorithm (Maximum Subarray) drops any negative prefix and tracks running local and global bests in one O(n) pass
  • Stack matching (Valid Parentheses) is the exact template for all nesting, balancing, and minimum-removal bracket problems
  • XOR pairing (Single Number) cancels duplicates algebraically, the signal to reach for bit tricks when the constraint says O(1) space
  • 1D DP Fibonacci recurrence (Climbing Stairs) is the cleanest intro to the state, recurrence, base case, space-optimization loop
  • Sentinel node and merge step (Merge Two Sorted Lists) are the building blocks for merge sort, k-way merge, and interval merging

Here's the thing nobody tells you: the warm-up problems will humble you just as fast as the hard ones. Not because they're tricky. Because you haven't actually loaded the pattern yet. You just think you have.

None of these ten problems are brutal. Each one teaches a reflex that shows up in harder problems. The goal isn't to crush them. It's to internalize what makes them fast, so that instinct is available when you're sitting across from an interviewer and your brain decides to briefly leave the building.

Use these before every serious practice session. Timed, 15 minutes each, no hints. Think of it as checking whether the engine actually turns over before you hit the highway.


1. Contains Duplicate Teaches You the Membership Reflex

LeetCode 217. Given an integer array, return true if any value appears at least twice.

The brute force is O(n²). You loop, you compare, you feel good about yourself, and then you submit and watch the time limit blow up. The fast path takes one read of the constraint: "does this element exist anywhere I've already been?" That's a hash set. A hash set answers membership in O(1).

def containsDuplicate(nums: list[int]) -> bool: seen = set() for num in nums: if num in seen: return True seen.add(num) return False

The reflex this builds: anytime a problem asks whether something appears more than once, your hand should already be reaching for a set. Every "find a duplicate," "check if two strings are anagrams," and "detect a cycle via values" problem runs on the same instinct. Once it's a reflex, you stop wasting the first three minutes rediscovering the wheel.

Time: O(n). Space: O(n). Should take under five minutes once the pattern is loaded.


2. Two Sum Teaches You Complement Lookup

LeetCode 1. Given an array and a target, return indices of two numbers that sum to the target.

Classic misdirection. Your first instinct is nested loops: try every pair. That's O(n²) and it works, but it's also how you get a polite "that's interesting, can you make it faster?" from your interviewer. The O(n) solution requires a small but non-obvious reframe: for each element x, you're not searching forward for a partner. You check whether target - x already exists in what you've already seen.

def twoSum(nums: list[int], target: int) -> list[int]: seen = {} for i, num in enumerate(nums): complement = target - num if complement in seen: return [seen[complement], i] seen[num] = i return []

The insight is that you're not looking forward, you're looking backward into what you've already stored. You process each element once, store it, and check against it later. This complement-lookup pattern appears in every "find a pair with property X" problem. Three Sum, Four Sum, and subarray problems all reach for it.


3. Best Time to Buy and Sell Stock Teaches Running Minimum

LeetCode 121. Given daily prices, find the maximum profit from one buy and one sell (buy before sell).

The nested loop is obvious and O(n²). Everyone writes it first. It works. Then you realize you're asking the same question at every index in the most inefficient way imaginable. The fast version asks a simpler question at each step: "what's the cheapest I could have bought before today?" Track that as a running minimum. Profit at day i is just price[i] - min_price.

def maxProfit(prices: list[int]) -> int: 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

One pass, O(1) space, and the pattern generalizes to any "best outcome relative to what I've seen so far" problem. Buy and sell with cooldown, with transaction fees, and various multi-transaction variants all extend this same scan. The running minimum is the pattern; the rest is bookkeeping.


4. Maximum Subarray Teaches Local vs Global Optimal

LeetCode 53. Find the contiguous subarray with the largest sum.

Kadane's algorithm is one of the cleanest ideas in DSA, which makes it annoying that it has exactly one footgun: initialization. The algorithm itself is elegant. At each index, the best subarray ending here is either this element alone, or this element plus whatever was optimal at the previous index. If the running sum goes negative, drop it. A negative prefix only hurts.

def maxSubArray(nums: list[int]) -> int: current = best = nums[0] for num in nums[1:]: current = max(num, current + num) best = max(best, current) return best

Initialize to nums[0], not zero. All-negative arrays break the zero initialization. This is one of those bugs that fails silently on most test cases and surfaces the second someone tests [-3, -1, -2]. Init to zero, return zero, look confused when the judge says wrong answer. Init to nums[0], be correct.

See Kadane's Algorithm for the full derivation.


5. Valid Parentheses Teaches the Stack-Matching Pattern

LeetCode 20. Given a string of brackets, determine if it's valid (every opener has a corresponding closer in order).

This one has the rare property of feeling trivially obvious once you know the answer and genuinely puzzling until you do. The stack pattern: push every opening bracket, and when you see a closing bracket, check whether the top of the stack matches. If it doesn't, or the stack is empty, invalid. If the stack has leftovers at the end, also invalid.

def isValid(s: str) -> bool: stack = [] pairs = {')': '(', ']': '[', '}': '{'} for ch in s: if ch in '([{': stack.append(ch) elif not stack or stack[-1] != pairs[ch]: return False else: stack.pop() return not stack

This exact structure solves every "matching/nesting" problem: HTML tag matching, balanced expressions, and the classic "minimum removals to make valid" problems all extend this template. Learn it once, clone it everywhere.


6. Single Number Teaches XOR Pairing

LeetCode 136. Every element appears twice except one. Find it in O(n) time and O(1) space.

The hash map solution is O(n) time but uses O(n) space. Fine, you'd pass. But the constraint "O(1) space" is a signal, not a suggestion. It means the interviewer is asking you to do something clever. In this case, the clever thing is XOR. XOR of any number with itself is zero. XOR of any number with zero is the number itself. So XOR every element together and the duplicates cancel, leaving the loner.

def singleNumber(nums: list[int]) -> int: result = 0 for num in nums: result ^= num return result

Once you know it, this is a 30-second problem. The broader skill is recognizing when integer constraints ("every element appears exactly twice," "values in a specific range") are hints toward O(1) space using bit tricks instead of extra storage. The constraint is the breadcrumb.


7. Binary Search Builds the Invariant Habit

LeetCode 704. Search for a target in a sorted array.

Binary search is simple to explain and deceptively easy to implement incorrectly under pressure. The usual suspects are the loop condition and the mid calculation. Use left + (right - left) // 2 to avoid integer overflow. Use while left <= right for exact-match search. These feel like small details until you get them wrong on problem 33 and spend ten minutes staring at a bug you've introduced into code you've written fifty times.

def search(nums: list[int], target: int) -> int: left, right = 0, len(nums) - 1 while left <= right: mid = left + (right - left) // 2 if nums[mid] == target: return mid elif nums[mid] < target: left = mid + 1 else: right = mid - 1 return -1

This problem earns its spot not because it's hard, but because getting it exactly right under pressure is harder than people expect. The canonical overflow bug (documented in Java's stdlib for nine years) lives inside a problem most engineers consider solved. Joshua Bloch, who wrote the original buggy version, called it "one of the most educational bugs I've ever seen." Nine years. In the standard library. Binary search is not trivial.

See Binary Search: One Invariant to Rule the Search Space for the boundary analysis.


8. Climbing Stairs Gets You Off the Ground With DP

LeetCode 70. You can climb 1 or 2 steps at a time. How many distinct ways to reach step n?

This is the Fibonacci sequence wearing a hat. The recurrence is f(n) = f(n-1) + f(n-2). The base cases are f(1) = 1, f(2) = 2. From there, you only need to remember the last two values, which means you can do the whole thing in O(1) space without a table.

def climbStairs(n: int) -> int: if n <= 2: return n prev2, prev1 = 1, 2 for _ in range(3, n + 1): prev2, prev1 = prev1, prev1 + prev2 return prev1

This is the cleanest possible introduction to the DP pattern: define your state, write the recurrence, identify the base cases, then optimize space. Coin change, house robber, and min cost climbing stairs all follow the same four steps. Internalize it here on the easy version so you're not figuring out the scaffolding when the problem gets harder.

See Dynamic Programming Is Just Recursion With a Memory for the full framework.


9. Reverse a Linked List Forces You to Think in Pointers

LeetCode 206. Reverse a singly linked list in place.

No tricks here. No clever observations. You need three pointers: the previous node (starts as null), the current node, and the next node saved before you overwrite the link. Work through one step on paper before you code it. You will get this wrong in your head and right on paper, every time, until the three-pointer dance is muscle memory.

def reverseList(head): prev, curr = None, head while curr: next_node = curr.next curr.next = prev prev = curr curr = next_node return prev

This problem is worth your time because pointer manipulation under pressure is a different skill from understanding pointers abstractly. Every "reverse a sublist," "reorder list," and "palindrome linked list" problem relies on the same three-pointer pattern. Do it until it feels mechanical. Then do it again.


10. Merge Two Sorted Lists Gives You the Merge Step

LeetCode 21. Merge two sorted linked lists into one sorted list.

The sentinel (dummy) head node is the move. It eliminates the edge case of an empty result list and means you don't have to special-case the first element. From there, compare the front of each list, take the smaller, advance that pointer, repeat. Drain whatever's left.

def mergeTwoLists(list1, list2): dummy = curr = ListNode(0) while list1 and list2: if list1.val <= list2.val: curr.next = list1 list1 = list1.next else: curr.next = list2 list2 = list2.next curr = curr.next curr.next = list1 or list2 return dummy.next

The sentinel node technique and the merge step itself show up in merge sort, merge k sorted lists, and interval merging problems. Once you see the pattern here, those harder problems become variations on a template you already own. The dummy head is one of those small tricks that, once you know it, you use everywhere.

See Linked List Interview Questions for the canonical pointer patterns.


The Pattern Behind All Ten

Each one has a wrong first instinct (nested loop, extra space, no structure) and a clean correction (hash map, running variable, bit trick, pointer). The correction always comes from reading the constraint. The constraint is not boilerplate. It's the answer in disguise.

The 15-minute clock is a diagnostic tool, not a goal. If you can't finish one of these in 15 minutes, that's data. It means the pattern isn't loaded yet, and you need more reps before it's automatic. No judgment. Just more reps.

Reps mean timed practice, not passive review. Close the solution, open a blank editor, write it from scratch. That's the difference between recognizing code and producing it under pressure. The interview tests the second one.

ProblemKey PatternCore Insight
Contains DuplicateHash set membershipO(1) lookup beats O(n) scan
Two SumComplement lookupStore what you've seen, check backward
Best Time to Buy StockRunning minimumTrack cheapest buy in one pass
Maximum SubarrayKadane's algorithmDrop negative prefix, continue
Valid ParenthesesStack matchingPush openers, validate closers
Single NumberXOR pairingDuplicates cancel, odd one survives
Binary Searchlo/hi invariantShrink the space, never revisit
Climbing Stairs1D DP / FibonacciState, recurrence, base cases, space opt
Reverse Linked ListThree-pointer swapSave next, redirect, advance
Merge Two Sorted ListsMerge stepSentinel head, compare fronts, drain tail

Your 15-Minute Warm-Up Routine

Run these before every serious practice session. Not as a confidence boost. As calibration. On a good day, all ten take under two hours. On a bad day, one of them sticks and tells you exactly where your pattern recognition has gaps. That's the point. Better to find out now than at 10am on interview day.

If you want to practice them in a real interview format, with a timer, follow-up questions, and rubric-based feedback on your communication, SpaceComplexity runs voice-based mock interviews that score you on the same dimensions real interviewers use.

For the next level after these, work through LeetCode Medium problems and the Top 50 LeetCode problems by pattern.


Further Reading