DEV Community

Cover image for I Struggled with LeetCode for Months Until I Learned These 6 Patterns 🎯
Rajat Parihar
Rajat Parihar

Posted on

I Struggled with LeetCode for Months Until I Learned These 6 Patterns 🎯

Hello Everyone! πŸ‘‹

I'm Rajat, a 3rd-year CS student, and let me be honest - LeetCode kicked my ass for months.

I'd open an problem, stare at it for 30 minutes, give up, look at the solution, and think "how was I supposed to think of that??" 😭

Sound familiar?

Here's what changed everything: I stopped grinding random problems and started learning PATTERNS.


πŸ€” What I Wish Someone Told Me on Day 1

Wrong approach (what I did):

  • Solve problem #1
  • Solve problem #2
  • Solve problem #3
  • Each one feels brand new 😰
  • Forget everything by next week

Right approach (what actually works):

  • Learn Pattern #1
  • Solve 5 problems using Pattern #1
  • Brain recognizes the pattern
  • New problems become variations of known patterns
  • Actually retain what you learned! 🧠

The truth: LeetCode has ~3000 problems, but only ~15 core patterns.

Master the patterns β†’ Solve most problems ✨


πŸ“š What This Guide Is

Full transparency:

  • I'm NOT at FAANG (yet! 🀞)
  • I've solved ~200 problems (not 2000)
  • I still look up solutions sometimes
  • BUT I finally understand HOW to approach problems

What you'll get:

  • βœ… 6 essential patterns (Part 1 of 2)
  • βœ… The intuition behind each pattern
  • βœ… When to use (and when NOT to use)
  • βœ… Real LeetCode problems explained
  • βœ… Complete code solutions
  • βœ… Common mistakes I made (so you don't!)
  • βœ… Student-friendly explanations (no jargon!)

Time investment: One weekend to learn
Difficulty: Beginner-friendly (I explain everything!)
Cost: $0 (LeetCode free tier is enough)

Note: This is Part 1 (Patterns 1-6). Part 2 coming next week with patterns 7-15!


πŸ“– Table of Contents


🎯 Pattern 1: Prefix Sum

Pattern 1: Prefix Sum

The Intuition (How I Finally Got It)

Picture this: You're at a restaurant tracking your expenses every day. Without prefix sum, every time your friend asks "how much did we spend from Monday to Wednesday?", you're adding numbers again. Exhausting, right?

The lightbulb moment for me: What if I just wrote down running totals?

Day 1: $10 (total so far: $10)
Day 2: $15 (total so far: $25)
Day 3: $20 (total so far: $45)

Now ANY question is instant subtraction! "Monday to Wednesday?" β†’ $45 - $0 = $45. Done. ✨

Core insight: Do the work ONCE upfront, answer infinite queries instantly.

What Even Is It?

Simple explanation: Pre-calculate all the sums so you don't have to recalculate them every time.

Real-world analogy:

Imagine you're tracking your daily expenses:

  • Day 1: $10
  • Day 2: $15
  • Day 3: $20

Without prefix sum (slow way):
"How much did I spend from Day 1 to Day 2?"
β†’ Calculate: 10 + 15 = 25

"How much from Day 1 to Day 3?"
β†’ Calculate again: 10 + 15 + 20 = 45

With prefix sum (smart way):
Pre-calculate running totals once:

  • Prefix[1] = 10
  • Prefix[2] = 25 (10+15)
  • Prefix[3] = 45 (10+15+20)

Now any range query is instant! Just subtraction.

When to Use This Pattern

Signs you need prefix sum:

  • βœ… Problem asks for sum of subarray/range
  • βœ… Multiple queries on the same array
  • βœ… Words like "range sum", "cumulative sum"
  • βœ… Need to answer queries efficiently

Real clue from problem: "Given an array, answer Q queries about sum in range [i, j]"

When NOT to use:

  • ❌ Only one query (not worth the preprocessing)
  • ❌ Streaming data (can't preprocess what you haven't seen)
  • ❌ Array changes frequently (prefix becomes invalid)

Core Concept

# Build prefix sum array
def build_prefix_sum(nums):
    prefix = [0]  # Start with 0 for easier calculation
    for num in nums:
        prefix.append(prefix[-1] + num)
    return prefix

# Example
nums = [1, 2, 3, 4, 5]
prefix = [0, 1, 3, 6, 10, 15]
#         ↑  ↑  ↑  ↑   ↑   ↑
#         0  1  1+2 1+2+3 ...

# Get sum from index i to j (0-indexed)
def range_sum(prefix, i, j):
    return prefix[j + 1] - prefix[i]

# Sum from index 1 to 3 (2+3+4)
result = prefix[4] - prefix[1]  # 10 - 1 = 9 βœ…
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 1: Range Sum Query - Immutable (#303)

Problem: Given an array, answer multiple sum queries efficiently.

Example:

Input: nums = [1, 2, 3, 4, 5]
Queries: 
- sumRange(0, 2) β†’ 1+2+3 = 6
- sumRange(1, 4) β†’ 2+3+4+5 = 14
Enter fullscreen mode Exit fullscreen mode

My Solution (Python):

class NumArray:
    def __init__(self, nums):
        """
        Build prefix sum array once during initialization.
        This is the "preprocessing" step!
        """
        # Start with [0] for easier calculation
        self.prefix = [0]

        # Build running sum
        for num in nums:
            self.prefix.append(self.prefix[-1] + num)

        # Example: nums = [1, 2, 3, 4, 5]
        # prefix = [0, 1, 3, 6, 10, 15]

    def sumRange(self, left, right):
        """
        Get sum from left to right in O(1) time!
        Magic formula: prefix[right + 1] - prefix[left]
        """
        return self.prefix[right + 1] - self.prefix[left]

# Usage
obj = NumArray([1, 2, 3, 4, 5])
print(obj.sumRange(0, 2))  # Output: 6
print(obj.sumRange(1, 4))  # Output: 14
Enter fullscreen mode Exit fullscreen mode

Why this works:

nums =   [1, 2, 3, 4, 5]
prefix = [0, 1, 3, 6, 10, 15]
         ↑     ↑        ↑
         0     left     right+1

Sum from index 1 to 3:
= prefix[4] - prefix[1]
= 10 - 1
= 9 (which is 2+3+4) βœ…
Enter fullscreen mode Exit fullscreen mode

Time Complexity:

  • Build prefix: O(n)
  • Each query: O(1) (instant!)
  • Without prefix: Each query would be O(n) 😱

Common Mistake #1: Forgetting the initial 0

# I did this and spent an HOUR debugging! 🀦
prefix = []  # WRONG - crashes on first element!
for num in nums:
    prefix.append(prefix[-1] + num)  # IndexError!

# RIGHT βœ…
prefix = [0]  # This 0 is your best friend!
Enter fullscreen mode Exit fullscreen mode

The 0 represents "sum before any elements" - it's not random! Think of it like asking "how much money before you earned anything?" The answer is $0. This base case makes everything work smoothly.


LeetCode Problem 2: Contiguous Array (#525)

The Intuition: When I first saw "equal 0s and 1s", I thought "count them in every subarray?" - that's O(nΒ²), way too slow!

Then it hit me: What if 0s and 1s cancelled each other out? Like +1 and -1 in math. If they're equal, sum = 0! 🀯

This transforms "equal count" into "sum = 0", which we KNOW how to solve with prefix sum!

Problem: Find longest subarray with equal number of 0s and 1s.

Example:

Input: [0, 1, 0]
Output: 2 (subarray [0, 1] or [1, 0])
Enter fullscreen mode Exit fullscreen mode

Key insight: Treat 0 as -1, then find subarray with sum = 0!

My Solution:

def findMaxLength(nums):
    """
    Trick: Replace 0 with -1, then find longest subarray with sum 0

    [0, 1, 0] β†’ [-1, 1, -1]

    If prefix sum appears twice, the subarray between them has sum 0!
    """
    # Track: prefix_sum β†’ first index where we saw it
    seen = {0: -1}  # Base case: sum 0 at index -1
    max_len = 0
    prefix_sum = 0

    for i, num in enumerate(nums):
        # Treat 0 as -1
        prefix_sum += 1 if num == 1 else -1

        # Have we seen this sum before?
        if prefix_sum in seen:
            # Length of subarray with sum 0
            length = i - seen[prefix_sum]
            max_len = max(max_len, length)
        else:
            # First time seeing this sum
            seen[prefix_sum] = i

    return max_len
Enter fullscreen mode Exit fullscreen mode

Visual walkthrough:

nums =       [0,  1,  1,  0]
as -1/1:     [-1, 1,  1, -1]

prefix_sum:   -1, 0,  1,  0
              ↑       ↑   ↑
              i=0     i=2 i=3

At i=1: sum=0 (seen at i=-1), length = 1-(-1) = 2
At i=3: sum=0 (seen at i=1), length = 3-1 = 2
Enter fullscreen mode Exit fullscreen mode

Common Mistake #2: The mysterious {0: -1}

# I forgot this and couldn't figure out edge cases for DAYS!
seen = {}  # WRONG - misses subarrays starting from index 0

# RIGHT βœ…
seen = {0: -1}  # -1 means "before the array starts"
Enter fullscreen mode Exit fullscreen mode

Think of -1 as the "starting line" in a race. When you see sum=0 at index 3, you calculate length = 3 - (-1) = 4. It's the number of steps from the start!


LeetCode Problem 3: Subarray Sum Equals K (#560)

The Intuition: This one broke my brain at first. Then I realized: if I want sum = k, I need to find where current_sum - k happened before. It's like reverse engineering!

If I'm at position with sum=10, and I want k=3, I look for where sum was 7. The part between 7 and 10? That's exactly 3! 🎯

Problem: Count number of subarrays with sum = k.

Example:

Input: nums = [1, 2, 3], k = 3
Output: 2 (subarrays: [1,2] and [3])
Enter fullscreen mode Exit fullscreen mode

My Solution:

def subarraySum(nums, k):
    """
    Key insight: If prefix_sum[j] - prefix_sum[i] = k
    Then subarray from i to j has sum k!

    Rearranging: prefix_sum[i] = prefix_sum[j] - k
    """
    # Track: prefix_sum β†’ how many times we've seen it
    seen = {0: 1}  # Base case
    count = 0
    prefix_sum = 0

    for num in nums:
        prefix_sum += num

        # Check if (prefix_sum - k) exists
        # If yes, we found subarray(s) with sum k!
        if (prefix_sum - k) in seen:
            count += seen[prefix_sum - k]

        # Update frequency
        seen[prefix_sum] = seen.get(prefix_sum, 0) + 1

    return count
Enter fullscreen mode Exit fullscreen mode

Why this works:

nums = [1, 2, 3], k = 3

prefix_sum: 1, 3, 6
            ↑  ↑

At i=1: prefix=3, check if (3-3=0) exists? 
        Yes! Count = 1 (subarray [1,2])

At i=2: prefix=6, check if (6-3=3) exists?
        Yes! Count = 2 (subarray [3])
Enter fullscreen mode Exit fullscreen mode

Common Mistake #3: Using set instead of counter

# I did this and got wrong answers! 🀦
seen = set([0])  # WRONG - only tracks existence
if target in seen:
    count += 1  # Always adds 1, even if target appeared multiple times!

# RIGHT βœ…
seen = {0: 1}  # Tracks frequency
if target in seen:
    count += seen[target]  # Adds correct count!
Enter fullscreen mode Exit fullscreen mode

Why frequency matters: If the same sum appeared 3 times before, we found 3 different subarrays! Don't lose that information.


🎯 Pattern 2: Two Pointers

Pattern 2: Two Pointers

The Intuition (My "Aha!" Moment)

Imagine two people at opposite ends of a hallway trying to find each other. If they both walk toward the middle, they'll meet WAY faster than one person checking every spot, right?

That's two pointers! We eliminate half the possibilities with each decision.

The genius: When sum is too small, we KNOW we need a bigger number (move left). When sum is too big, we KNOW we need a smaller number (move right). We're not guessing - we're intelligently shrinking the search space.

What Even Is It?

Simple explanation: Use two pointers moving toward each other (or same direction) to solve problems.

Real-world analogy:

Imagine two people searching for each other in a hallway:

  • Person A starts at left end
  • Person B starts at right end
  • They walk toward each other until they meet

Much faster than one person checking every spot! πŸšΆβ€β™‚οΈπŸšΆβ€β™€οΈ

When to Use This Pattern

Signs you need two pointers:

  • βœ… Sorted array (or can be sorted)
  • βœ… Need to find pairs/triplets
  • βœ… Need to compare elements from both ends
  • βœ… Problem mentions "opposite ends" or "pairs"

When NOT to use:

  • ❌ Order matters and array can't be sorted
  • ❌ Need to check ALL combinations (not just pairs)
  • ❌ Elements don't have a comparison relationship

Core Concept

# Two pointers approaching from opposite ends
def two_pointer_opposite(nums, target):
    left = 0
    right = len(nums) - 1

    while left < right:
        current_sum = nums[left] + nums[right]

        if current_sum == target:
            return [left, right]  # Found it!
        elif current_sum < target:
            left += 1  # Need bigger sum
        else:
            right -= 1  # Need smaller sum

    return [-1, -1]  # Not found
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 1: Two Sum II (#167)

The Intuition: Since the array is sorted, we get a superpower: We can make smart decisions about which pointer to move. Too small? Move left (get bigger). Too big? Move right (get smaller). We're not randomly trying - we're eliminating wrong answers!

Problem: Find two numbers in SORTED array that add to target.

Example:

Input: numbers = [2, 7, 11, 15], target = 9
Output: [1, 2] (indices 1 and 2, 1-indexed)
Enter fullscreen mode Exit fullscreen mode

My Solution:

def twoSum(numbers, target):
    """
    Since array is SORTED, we can use two pointers!

    Key insight:
    - If sum < target, left pointer moves right (increase sum)
    - If sum > target, right pointer moves left (decrease sum)
    """
    left = 0
    right = len(numbers) - 1

    while left < right:
        current_sum = numbers[left] + numbers[right]

        if current_sum == target:
            # Found it! (1-indexed)
            return [left + 1, right + 1]
        elif current_sum < target:
            # Need larger sum, move left pointer right
            left += 1
        else:
            # Need smaller sum, move right pointer left
            right -= 1

    return [-1, -1]
Enter fullscreen mode Exit fullscreen mode

Visual walkthrough:

numbers = [2, 7, 11, 15], target = 9
           ↑           ↑
         left        right

Step 1: 2 + 15 = 17 > 9, move right left
Step 2: 2 + 11 = 13 > 9, move right left
Step 3: 2 + 7 = 9 βœ… Found it!
Enter fullscreen mode Exit fullscreen mode

Common Mistake #4: Forgetting 1-indexed output

# I returned [0, 1] and spent 20 minutes wondering why it failed! 🀦
return [left, right]  # WRONG - problem wants 1-indexed!

# RIGHT βœ…
return [left + 1, right + 1]  # Always read the problem carefully!
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 2: 3Sum (#15)

The Intuition: This is just Two Sum... but 3 times! Fix one number, then use two pointers for the remaining two. The trick: skip duplicates aggressively, or you'll get the same triplet multiple times.

Problem: Find all unique triplets that sum to zero.

Example:

Input: nums = [-1, 0, 1, 2, -1, -4]
Output: [[-1, -1, 2], [-1, 0, 1]]
Enter fullscreen mode Exit fullscreen mode

My Solution:

def threeSum(nums):
    """
    3Sum = Fix one number + Two Sum on remaining

    Steps:
    1. Sort array
    2. Fix first number
    3. Use two pointers for remaining two
    4. Skip duplicates!
    """
    nums.sort()  # MUST sort first!
    result = []

    for i in range(len(nums) - 2):
        # Skip duplicates for first number
        if i > 0 and nums[i] == nums[i - 1]:
            continue

        # Two pointers for remaining array
        left = i + 1
        right = len(nums) - 1
        target = -nums[i]

        while left < right:
            current_sum = nums[left] + nums[right]

            if current_sum == target:
                result.append([nums[i], nums[left], nums[right]])

                # Skip duplicates for second number
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                # Skip duplicates for third number
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1

                left += 1
                right -= 1
            elif current_sum < target:
                left += 1
            else:
                right -= 1

    return result
Enter fullscreen mode Exit fullscreen mode

Common Mistake #5: Not skipping duplicates

# My first solution had duplicate triplets everywhere! 🀦
# WRONG - no duplicate handling
for i in range(len(nums) - 2):
    # No skip here
    left = i + 1
    ...

# RIGHT βœ…
for i in range(len(nums) - 2):
    if i > 0 and nums[i] == nums[i - 1]:
        continue  # Skip duplicates!
Enter fullscreen mode Exit fullscreen mode

Without skipping: Input [-1, -1, 2] gives you [[-1, -1, 2], [-1, -1, 2]]. With skipping: Just [[-1, -1, 2]]. Clean!


LeetCode Problem 3: Container With Most Water (#11)

The Intuition: Start with maximum width. Now, the height is limited by the shorter line. If we move the shorter line, height MIGHT increase. If we move the taller line, height can ONLY stay same or decrease (limited by the other line). Always move the shorter line - it's our bottleneck!

Problem: Find two lines that contain the most water.

My Solution:

def maxArea(height):
    """
    Key insight: 
    Area = width Γ— min(left_height, right_height)

    Start with maximum width, then try to find taller lines
    """
    left = 0
    right = len(height) - 1
    max_water = 0

    while left < right:
        # Calculate current area
        width = right - left
        current_height = min(height[left], height[right])
        current_area = width * current_height

        max_water = max(max_water, current_area)

        # Move the pointer with smaller height
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1

    return max_water
Enter fullscreen mode Exit fullscreen mode

Why move smaller height?

height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
          ↑                       ↑
        left=1                 right=7

If we move right pointer:
- Width decreases
- Height still limited by left=1
- Area can only get WORSE!

If we move left pointer:
- Width decreases
- But height might increase!
- Chance of better area βœ…
Enter fullscreen mode Exit fullscreen mode

🎯 Pattern 3: Sliding Window

Pattern 3: Sliding Window

The Intuition (The Train Window Analogy)

Imagine you're on a train looking out a window. As the train moves:

  • New tree enters your view (add to window)
  • Old tree exits your view (remove from window)
  • You don't recount ALL trees each time - just update!

That's sliding window! Instead of recalculating from scratch, we adjust what we already know.

What Even Is It?

Simple explanation: Maintain a "window" of elements and slide it through the array.

When to Use This Pattern

Signs you need sliding window:

  • βœ… Contiguous subarray/substring
  • βœ… "Maximum/minimum of subarray of size k"
  • βœ… "Longest substring with condition"
  • βœ… Need to track elements in a range

When NOT to use:

  • ❌ Non-contiguous elements (use dp or backtracking)
  • ❌ Need global information (not just local window)

Core Concept

# Fixed size window
def fixed_window(nums, k):
    window_sum = sum(nums[:k])  # Initial window
    max_sum = window_sum

    for i in range(k, len(nums)):
        # Slide window: remove left, add right
        window_sum = window_sum - nums[i - k] + nums[i]
        max_sum = max(max_sum, window_sum)

    return max_sum
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 1: Maximum Average Subarray I (#643)

The Intuition: Instead of summing 4 elements every time (slow!), what if we just remove the element leaving the window and add the element entering? It's like a sliding belt - same size, just moving! O(n Γ— k) becomes O(n).

Problem: Find contiguous subarray of size k with maximum average.

My Solution:

def findMaxAverage(nums, k):
    """
    Fixed size sliding window

    Instead of recalculating sum each time:
    - Remove leftmost element
    - Add rightmost element
    """
    window_sum = sum(nums[:k])
    max_sum = window_sum

    for i in range(k, len(nums)):
        window_sum = window_sum - nums[i - k] + nums[i]
        max_sum = max(max_sum, window_sum)

    return max_sum / k
Enter fullscreen mode Exit fullscreen mode

Common Mistake #6: Recalculating sum each time

# This is O(n Γ— k) - TOO SLOW! 🐌
for i in range(len(nums) - k + 1):
    current_sum = sum(nums[i:i+k])  # WRONG - summing every time!

# RIGHT βœ… - O(n)
window_sum = sum(nums[:k])  # Sum once
for i in range(k, len(nums)):
    window_sum = window_sum - nums[i-k] + nums[i]  # Just adjust!
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 2: Longest Substring Without Repeating (#3)

The Intuition: Expand the window greedily until you hit a duplicate. Then shrink from the left until the duplicate is gone. It's like a caterpillar inching forward! πŸ›

Problem: Find longest substring without repeating characters.

My Solution:

def lengthOfLongestSubstring(s):
    """
    Variable size sliding window

    Expand window until duplicate
    Then shrink from left until no duplicate
    """
    char_set = set()
    left = 0
    max_length = 0

    for right in range(len(s)):
        # If duplicate, shrink window from left
        while s[right] in char_set:
            char_set.remove(s[left])
            left += 1

        char_set.add(s[right])
        max_length = max(max_length, right - left + 1)

    return max_length
Enter fullscreen mode Exit fullscreen mode

Common Mistake #7: Using list instead of set

# I did this and got O(n²) runtime! 🀦
chars = []  # WRONG - checking "in list" is O(n)
if s[right] in chars:  # This is slow!

# RIGHT βœ…
char_set = set()  # Checking "in set" is O(1)
if s[right] in char_set:  # Fast!
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 3: Minimum Window Substring (#76)

The Intuition: This one's tough. Expand until you have all required characters. Then shrink as much as possible while staying valid. Track the minimum window you find. It's like finding the tightest fit!

Problem: Find minimum window containing all characters of t.

My Solution:

def minWindow(s, t):
    """
    1. Expand window until all characters found
    2. Shrink window while still valid
    3. Track minimum window
    """
    if not s or not t:
        return ""

    need = {}
    for char in t:
        need[char] = need.get(char, 0) + 1

    have = {}
    left = 0
    min_len = float('inf')
    min_start = 0
    required = len(need)
    formed = 0

    for right in range(len(s)):
        char = s[right]
        have[char] = have.get(char, 0) + 1

        if char in need and have[char] == need[char]:
            formed += 1

        # Try to shrink while valid
        while formed == required:
            if right - left + 1 < min_len:
                min_len = right - left + 1
                min_start = left

            left_char = s[left]
            have[left_char] -= 1
            if left_char in need and have[left_char] < need[left_char]:
                formed -= 1

            left += 1

    return "" if min_len == float('inf') else s[min_start:min_start + min_len]
Enter fullscreen mode Exit fullscreen mode

This problem took me 3 days to understand. Don't feel bad if it doesn't click immediately! πŸ˜…


🎯 Pattern 4: Fast & Slow Pointers

Pattern 4: Fast & Slow Pointers

The Intuition (The Race Track)

Two runners on a circular track: one slow, one fast. If the track loops back (cycle), the fast runner will lap the slow one - they'll meet! If the track ends, the fast runner finishes first - no cycle.

This is Floyd's algorithm, but I think of it as "the race detective" πŸ”

What Even Is It?

Simple explanation: Two pointers moving at different speeds (one fast, one slow).

When to Use This Pattern

Signs you need fast & slow:

  • βœ… Detect cycles
  • βœ… Find middle of linked list
  • βœ… Problems on linked lists with potential cycles

When NOT to use:

  • ❌ Array problems (usually two pointers work better)
  • ❌ Need exact position information (not just cycle detection)

LeetCode Problem 1: Linked List Cycle (#141)

The Intuition: If there's a cycle, fast eventually catches slow (runners on circular track). If there's no cycle, fast reaches the end (track has an end). Beautiful, right? ✨

My Solution:

def hasCycle(head):
    """
    Fast moves 2 steps, slow moves 1 step
    If cycle exists, they'll meet
    """
    if not head:
        return False

    slow = head
    fast = head

    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

        if slow == fast:
            return True

    return False
Enter fullscreen mode Exit fullscreen mode

Common Mistake #8: Not checking fast.next

# This crashes with AttributeError! 🀦
while fast:  # WRONG - need to check fast.next too!
    fast = fast.next.next  # Might be None!

# RIGHT βœ…
while fast and fast.next:
    fast = fast.next.next
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 2: Happy Number (#202)

The Intuition: Either the number reaches 1 (happy!) or it enters a cycle (not happy). Instead of tracking all seen numbers, use fast & slow to detect the cycle! Same race track idea.

My Solution:

def isHappy(n):
    def get_next(num):
        total = 0
        while num > 0:
            digit = num % 10
            total += digit ** 2
            num //= 10
        return total

    slow = n
    fast = n

    while True:
        slow = get_next(slow)
        fast = get_next(get_next(fast))

        if fast == 1:
            return True

        if slow == fast:
            return False
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 3: Find the Duplicate Number (#287)

The Intuition: Mind-bending one! Treat the array like a linked list where nums[i] points to nums[nums[i]]. If there's a duplicate, there's a cycle! Use cycle detection to find where the cycle starts (the duplicate).

My Solution:

def findDuplicate(nums):
    # Phase 1: Detect cycle
    slow = nums[0]
    fast = nums[0]

    while True:
        slow = nums[slow]
        fast = nums[nums[fast]]
        if slow == fast:
            break

    # Phase 2: Find entrance (duplicate)
    slow = nums[0]
    while slow != fast:
        slow = nums[slow]
        fast = nums[fast]

    return slow
Enter fullscreen mode Exit fullscreen mode

This one's genius but confusing! The math behind it is Floyd's cycle detection. Trust the algorithm! 😎


🎯 Pattern 5: LinkedList In-Place Reversal

Pattern 5: LinkedList In-Place Reversal

The Intuition (The Hand-Holding Flip)

Imagine people holding hands in a line: A→B→C→D

To reverse: Make B let go of C and grab A instead. Then C let go of D and grab B. One by one, change who's holding whose hand. No need for a second line of people!

What Even Is It?

Simple explanation: Reverse linked list by changing pointers, without extra space.

When to Use This Pattern

Signs you need in-place reversal:

  • βœ… "Reverse linked list"
  • βœ… "Reverse from position m to n"
  • βœ… Must be O(1) space

LeetCode Problem 1: Reverse Linked List (#206)

The Intuition: Keep three pointers (prev, curr, next). Break the forward link, create backward link, move forward. Like dominoes falling backward!

My Solution:

def reverseList(head):
    prev = None
    curr = head

    while curr:
        next_temp = curr.next  # Save next
        curr.next = prev       # Reverse
        prev = curr            # Move prev
        curr = next_temp       # Move curr

    return prev
Enter fullscreen mode Exit fullscreen mode

Common Mistake #9: Forgetting to save next

# I lost track of the rest of the list! 🀦
curr.next = prev  # WRONG - we just lost the rest of the list!

# RIGHT βœ…
next_temp = curr.next  # Save first!
curr.next = prev
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 2: Reverse Linked List II (#92)

Problem: Reverse from position m to n.

My Solution:

def reverseBetween(head, m, n):
    if not head or m == n:
        return head

    dummy = ListNode(0)
    dummy.next = head
    pre = dummy

    # Find position m-1
    for _ in range(m - 1):
        pre = pre.next

    # Reverse from m to n
    curr = pre.next
    for _ in range(n - m):
        temp = curr.next
        curr.next = temp.next
        temp.next = pre.next
        pre.next = temp

    return dummy.next
Enter fullscreen mode Exit fullscreen mode

The dummy node trick makes edge cases SO much easier! It's like having a safety net.


LeetCode Problem 3: Swap Nodes in Pairs (#24)

My Solution:

def swapPairs(head):
    dummy = ListNode(0)
    dummy.next = head
    prev = dummy

    while prev.next and prev.next.next:
        first = prev.next
        second = prev.next.next

        # Swap
        first.next = second.next
        second.next = first
        prev.next = second

        prev = first

    return dummy.next
Enter fullscreen mode Exit fullscreen mode

🎯 Pattern 6: Monotonic Stack

Pattern 6: Monotonic Stack

The Intuition (The Height Police)

Imagine stacking books by height. New short book? Remove all taller books first to maintain order. The stack "polices" the height requirement!

When we pop a book, we found its "next taller" or "next shorter" element. That's the magic - the popping IS the answer!

What Even Is It?

Simple explanation: Stack that maintains increasing or decreasing order.

When to Use This Pattern

Signs you need monotonic stack:

  • βœ… "Next greater/smaller element"
  • βœ… "Previous greater/smaller"
  • βœ… Histogram/stock span problems

LeetCode Problem 1: Next Greater Element I (#496)

The Intuition: As we see each number, check if it's greater than numbers waiting in the stack. If yes, we found their "next greater"! Pop them and record.

My Solution:

def nextGreaterElement(nums1, nums2):
    next_greater = {}
    stack = []

    for num in nums2:
        while stack and num > stack[-1]:
            smaller = stack.pop()
            next_greater[smaller] = num
        stack.append(num)

    for num in stack:
        next_greater[num] = -1

    return [next_greater[num] for num in nums1]
Enter fullscreen mode Exit fullscreen mode

Common Mistake #10: Storing values instead of indices

# Sometimes you need indices, not values!
stack.append(num)  # WRONG if you need position!

# RIGHT βœ…
stack.append(i)  # Store index!
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 2: Daily Temperatures (#739)

My Solution:

def dailyTemperatures(temperatures):
    n = len(temperatures)
    result = [0] * n
    stack = []  # Store indices

    for i in range(n):
        while stack and temperatures[i] > temperatures[stack[-1]]:
            prev_day = stack.pop()
            result[prev_day] = i - prev_day
        stack.append(i)

    return result
Enter fullscreen mode Exit fullscreen mode

LeetCode Problem 3: Largest Rectangle in Histogram (#84)

My Solution:

def largestRectangleArea(heights):
    stack = []
    max_area = 0
    heights.append(0)  # Sentinel

    for i in range(len(heights)):
        while stack and heights[i] < heights[stack[-1]]:
            h_idx = stack.pop()
            h = heights[h_idx]
            w = i if not stack else i - stack[-1] - 1
            max_area = max(max_area, h * w)
        stack.append(i)

    return max_area
Enter fullscreen mode Exit fullscreen mode

This one's the hardest! Don't worry if it takes multiple attempts. πŸ’ͺ


πŸŽ“ How to Practice These Patterns

Week 1-2: Foundation

  • [ ] Pattern 1 (Prefix Sum): 3 problems
  • [ ] Pattern 2 (Two Pointers): 3 problems
  • [ ] Focus on understanding WHY, not memorizing

Week 3-4: Build Momentum

  • [ ] Pattern 3 (Sliding Window): 3 problems
  • [ ] Pattern 4 (Fast & Slow): 3 problems
  • [ ] Start timing yourself (20-30 min per problem)

Week 5-6: Master It

  • [ ] Pattern 5 (Reversal): 3 problems
  • [ ] Pattern 6 (Monotonic Stack): 3 problems
  • [ ] Try variations of each pattern

My Study Routine

Daily (1 hour):

  • 30 min: New problem
  • 20 min: Review yesterday's problem
  • 10 min: Notes on what I learned

Weekend (2 hours):

  • Review week's patterns
  • Do contest problems
  • Watch explanations for problems I couldn't solve

πŸ› Mistakes I Made (So You Don't Have To)

Mistake #1: Memorizing Solutions

What I did: Memorized code
What happened: Forgot in a week
What works: Understand the PATTERN

Mistake #2: Skipping Easy Problems

What I did: "Easy is boring"
What happened: Failed medium problems
What works: Master easy first, build confidence

Mistake #3: Not Drawing It Out

What I did: Solved in my head
What happened: Made silly mistakes
What works: Draw diagrams, trace examples

Mistake #4: Looking at Solutions Too Fast

What I did: Struggled 5 min β†’ gave up
What happened: Learned nothing
What works: Struggle 30-45 min, look at HINTS first

Mistake #5: Not Reviewing

What I did: Solved once, never again
What happened: Forgot everything
What works: Review after 1 day, 1 week, before interview


πŸ’¬ Let's Connect!

We covered 6 essential patterns:

  1. βœ… Prefix Sum
  2. βœ… Two Pointers
  3. βœ… Sliding Window
  4. βœ… Fast & Slow Pointers
  5. βœ… LinkedList Reversal
  6. βœ… Monotonic Stack

Part 2 coming next week with patterns 7-15!

If this helped you:

  • ❀️ Like it
  • πŸ’¬ Comment which pattern clicked for you
  • πŸ”„ Share with friends
  • πŸ“š Bookmark for reference

Questions? Drop them below! I answer every single one! πŸ™Œ


Made with ❀️, lots of debugging, and way too much coffee by Rajat

3rd Year CS Student | Still Learning | Probably Solving LeetCode Right Now


P.S. - Don't worry if patterns don't click immediately. Took me weeks to truly GET them. Keep practicing! πŸ’ͺ

P.P.S. - Part 2 drops next week with Tree patterns, DFS/BFS, and DP. Follow so you don't miss it! πŸš€

Top comments (0)