20 LeetCode Problems That Teach Edge-Case Thinking

June 7, 202610 min read
dsaalgorithmsinterview-prepleetcode
20 LeetCode Problems That Teach Edge-Case Thinking
TL;DR
  • Initialize to nums[0], not 0: Maximum Subarray and similar problems return wrong results on all-negative inputs when the neutral value implies an empty subarray is valid.
  • Integer overflow in binary search: (lo+hi)//2 wraps in Java and C++; use lo + (hi-lo)//2 to eliminate it.
  • Track both min and max in product subarrays: A negative times a negative flips the minimum into the new maximum, so carrying only the running max misses valid answers.
  • Read the constraint block before coding: Find the Duplicate Number hides the O(1)-space algorithm inside the [1,n] constraint, not the problem statement.
  • The contained-interval bug in Merge Intervals: Using interval[1] instead of max(current_end, interval[1]) shrinks merges and silently fails on fully nested intervals.
  • k %= n is the first line in rotation problems: Any k ≥ n reduces to a smaller shift; skipping this causes wrong results when k equals the array length.
  • Three edge case families: Most LeetCode failures are neutral-value traps, constraint-driven special cases, or off-by-one and overflow errors.

You solved it. The algorithm is correct. The complexity checks out, you talked through your approach, the interviewer gave a small nod. You run it on their test case. It returns 0. The right answer is -3.

You initialized your running maximum to zero. Again.

The algorithm gets you 80% of the way. Edge cases are the other 20%. And unlike the algorithm, edge-case thinking is trainable. Certain problems teach it better than others because the naive solution almost works and the failure mode reveals something transferable.

Here are the 20 that do it most efficiently.


The Full List at a Glance

#ProblemLeetCodeThe Edge Case That Gets You
1Maximum Subarray53All-negative array: init to nums[0], never 0
2Binary Search704(lo+hi)//2 overflows; loop bound decides if last element is checked
3Valid Parentheses20Stack must be empty at end, not just during processing
4Rotate Array189k can exceed n; always k %= n first
5Reverse Integer7Check 32-bit bounds before appending each digit
63Sum15Skip duplicates after finding each triplet or you get duplicate results
7Decode Ways91"0" alone is invalid; "30" exceeds 26 and kills the preceding digit
8Maximum Product Subarray152Track both min and max; a negative times a negative turns positive
9Merge Intervals56Contained intervals: end must be max(current_end, next_end), not next_end
10Trapping Rain Water42Arrays shorter than 3 return 0; monotone arrays trap nothing
11Find the Duplicate Number287The [1,n] constraint is the whole trick; values are valid indices
12Product of Array Except Self238Two zeros make every element zero; one zero makes all non-zero positions zero
13Valid Palindrome125Empty string after filtering is a palindrome; comparison must be case-insensitive
14Jump Game55Single element is always reachable; last element needs no jump value at all
15Search in Rotated Sorted Array33No rotation is a valid case; handle when nums[lo] <= nums[mid] covers both halves
16Minimum Window Substring76len(s) < len(t) has no window; target empty string is technically valid
17Container With Most Water11Width is right - left, never right - left + 1; easy to confuse with 1-indexed counting
18Serialize/Deserialize Binary Tree297A null tree serializes to a non-empty string; skewed trees expand token count dramatically
19Course Schedule207Self-loop at any node is an immediate cycle; zero courses is always schedulable
20Palindrome Number9Negative numbers are never palindromes; any number ending in 0 (except 0 itself) is not

Seven That Bite Smart People Repeatedly

These seven have failure modes subtle enough to show up in senior engineer interviews. The rest you can absorb through practice. Start here.

Maximum Subarray: The Init Trap Will Find You

Initialize max_current = nums[0], never 0. Starting at zero implicitly allows an empty subarray. The problem requires at least one element. An all-negative array should return its least-negative value, not zero, and initializing to zero is you lying to your own algorithm about what the input looks like.

def max_subarray(nums: list[int]) -> int: max_current = max_global = nums[0] for num in nums[1:]: max_current = max(num, max_current + num) max_global = max(max_global, max_current) return max_global

The lesson generalizes: whenever you initialize a running optimum, ask what happens when every element is negative. A neutral starting value often hides an assumption about the data. That assumption will be wrong on test case 23.

Binary Search: Two Independent Bugs in One Function

Two separate edge cases, same function. First, the integer overflow bug that lived in Java's standard library for nine years: mid = (lo + hi) // 2 wraps when lo + hi exceeds the integer maximum. Fix it with lo + (hi - lo) // 2. Python integers are arbitrary-precision, so this is a Java and C++ problem specifically. The Java fix shipped in 2006. The bug was introduced in 1986.

Second, the termination condition. while lo < hi and while lo <= hi are not interchangeable. Each requires a different placement of lo = mid + 1 vs lo = mid. Pick one invariant and never mix them. The binary search off-by-one error covers both forms in detail.

Decode Ways: Zeros Will Kill You

Decode Ways (LC 91) is the canonical edge-case DP problem. The string "0" alone has zero decodings. "30" also has zero decodings because 30 exceeds 26. The trap is "10" and "20": you can decode the pair together (J and T), but the trailing "0" has no single-digit decoding. So dp[i] cannot add dp[i-1] when s[i] == '0'.

def num_decodings(s: str) -> int: if not s or s[0] == '0': return 0 dp = [0] * (len(s) + 1) dp[0] = 1 dp[1] = 1 for i in range(2, len(s) + 1): one_digit = int(s[i-1]) two_digits = int(s[i-2:i]) if one_digit != 0: dp[i] += dp[i-1] if 10 <= two_digits <= 26: dp[i] += dp[i-2] return dp[-1]

Every value constraint maps to a guard in your DP transitions. List the constraints explicitly before writing a single line of state logic. Skipping this step is how you spend 20 minutes debugging a solution that was always going to be wrong.

Maximum Product Subarray: When Tracking the Maximum Isn't Enough

Tracking only the maximum running product fails. A large negative product multiplied by another negative number becomes the array maximum. You have to track both the current minimum and current maximum at every step, then swap them when the next number is negative.

This generalizes to any operation that can flip ordering: negation, division by a negative, modular arithmetic. Carry both extremes. Ignoring the minimum because "we want the maximum" is the bug.

Merge Intervals: The Contained Interval Destroys Your Merge

Given [1,10] and [2,3], the merged result is [1,10]. Write merged[-1][1] = interval[1] instead of merged[-1][1] = max(merged[-1][1], interval[1]) and the contained interval shrinks your merge down to [1,3]. The smaller interval ate the larger one.

Always take the max of both endpoints. Most test cases show intervals that extend each other, not intervals nested inside another. This bug passes all your informal tests and fails in the judge on case 47.

Find the Duplicate Number: It Was in the Constraints the Whole Time

The problem looks like it wants a hash set. O(n) time, O(n) space, done. Except the constraints say the array has n+1 integers each in [1, n], meaning every value is a valid index. That turns the array into a linked list where nums[i] points to the next node, and the duplicate creates a cycle. Floyd's cycle detection solves it in O(n) time and O(1) space.

The constraint [1, n] is the algorithm. Reading the constraint block is design work, not background reading you skim past to get to the fun part.

Rotate Array: One Mod Before Everything Else

k = 6 on an array of length 3 is k = 0. Write k %= len(nums) as the first line. If k becomes 0, return early.

Simple once you see it. Easy to miss when you read "rotate by k steps" and k feels like a forward shift count. Six steps on a three-element array is just standing still.


Most LeetCode Edge Cases Fall Into Three Families

Across all 20 problems, the failures cluster into three categories. Recognizing which family you're in is most of the fix.

Neutral value traps. You initialize something to 0, True, or an empty string when the correct neutral depends on the data. Maximum Subarray, Maximum Product Subarray, and Valid Palindrome all do this. The question to ask: "Does my initial value make a silent assumption about the input?" If the answer involves the word "probably," you have a trap.

Constraint-driven special cases. The problem hides something in the constraint block that invalidates the naive approach entirely. Find the Duplicate Number uses [1, n]. Decode Ways uses [1, 26] for valid codes. Rotate Array uses k mod n. Constraints are algorithm design. Reading them as fine print is how you get surprised.

Off-by-one and overflow. Binary Search and Reverse Integer are the canonical cases. Container With Most Water (width is right - left, not right - left + 1) is close behind. The integer overflow article covers the Java-specific version in depth.

The useful thing about families: once you recognize the type, you know the fix before you know the bug. Seeing a running optimum means asking about neutral values. Seeing a value range in the constraints means asking whether it changes the data structure. Seeing an index calculation means asking whether your bounds match your loop invariant.


The Five-Minute Habit That Builds This Skill

After you reach a correct solution, spend five minutes on the edge cases checklist:

  1. What if the input is empty?
  2. What if it has one element?
  3. What if all elements are equal?
  4. What if all elements are negative?
  5. What if the answer is 0, false, or null?
  6. What if a value is at or near the integer limits?

Do it every time, not just on problems that look tricky. Pattern recognition transfers. You stop getting surprised.

The goal isn't to memorize 20 failure modes. It's to build the reflex of asking the question. Most interviewers aren't waiting for you to produce a perfect solution. They're watching whether you think to check the thing that will break it.

If you want to practice catching these under pressure, SpaceComplexity runs voice interviews where the interviewer follows up specifically on edge cases, then gives rubric-based feedback on whether you caught them before being prompted. It's faster feedback than a failed submission and closer to the real thing than running test cases in silence.


Further Reading