NeetCode 150 — Arrays & Hashing + Sliding Window — Solutions

Simplest-optimal Python solutions with intuition, memory hooks, and pressure tips. Read once; re-implement the next day from the hook alone.


Arrays & Hashing (9)

1. Contains Duplicate (LC 217)

Problem. Return True if any value appears at least twice.

Intuition. Compare set size to list size; equal means all unique.

def containsDuplicate(nums):
    return len(set(nums)) != len(nums)

Complexity. time, space.

Hook. "Set vs len."


2. Valid Anagram (LC 242)

Problem. Are s and t anagrams?

Intuition. Same character counts.

from collections import Counter
def isAnagram(s, t):
    return Counter(s) == Counter(t)

Complexity. time, space (26 chars).

Hook. "Counter equal."

Watch out. Different lengths → False (Counter handles this).


3. Two Sum (LC 1)

Problem. Indices of two numbers summing to target.

Intuition. As you scan, ask "have I already seen target - x?" Hash map gives lookup.

def twoSum(nums, target):
    seen = {}
    for i, x in enumerate(nums):
        if target - x in seen:
            return [seen[target - x], i]
        seen[x] = i

Complexity. time, space.

Hook. "Have I seen the complement."

Watch out. Insert after checking, so nums[i] + nums[i] == target doesn't return [i, i].


4. Group Anagrams (LC 49)

Problem. Group strings that are anagrams of each other.

Intuition. Anagrams share a canonical key — sorted chars or 26-char-count tuple.

from collections import defaultdict
def groupAnagrams(strs):
    groups = defaultdict(list)
    for s in strs:
        key = tuple(sorted(s))   # or [count of each char]
        groups[key].append(s)
    return list(groups.values())

Complexity. with sorted key (k = max length); with count tuple.

Hook. "Hash by canonical form."


5. Top K Frequent Elements (LC 347)

Problem. Return the most frequent elements.

Intuition. Bucket sort: index = frequency, value = list of elements with that frequency. Walk from high to low. .

from collections import Counter
def topKFrequent(nums, k):
    freq = Counter(nums)
    buckets = [[] for _ in range(len(nums) + 1)]
    for x, c in freq.items():
        buckets[c].append(x)
    out = []
    for c in range(len(buckets) - 1, 0, -1):
        out.extend(buckets[c])
        if len(out) >= k:
            return out[:k]

Complexity. time and space.

Hook. "Frequency-as-index bucket."

Alt. Heap of size . Bucket sort is cleaner here.


6. Encode and Decode Strings (LC 271)

Problem. Encode list of strings to one string; decode back.

Intuition. Length-prefix each string with delimiter so decoder knows where each ends.

def encode(strs):
    return ''.join(f"{len(s)}#{s}" for s in strs)

def decode(s):
    out, i = [], 0
    while i < len(s):
        j = s.index('#', i)
        L = int(s[i:j])
        out.append(s[j+1:j+1+L])
        i = j + 1 + L
    return out

Complexity. both.

Hook. "Length#string."

Watch out. Strings can contain #, but length-prefix makes that safe — you read exactly L chars after the #.


7. Product of Array Except Self (LC 238)

Problem. out[i] = product of all elements except nums[i]. No division.

Intuition. Two passes: prefix product from left, then suffix product from right multiplied in.

def productExceptSelf(nums):
    n = len(nums)
    out = [1] * n
    pre = 1
    for i in range(n):
        out[i] = pre
        pre *= nums[i]
    suf = 1
    for i in range(n - 1, -1, -1):
        out[i] *= suf
        suf *= nums[i]
    return out

Complexity. time, extra (output not counted).

Hook. "Left prefix, right suffix, multiply in place."


8. Valid Sudoku (LC 36)

Problem. Is the partially filled 9x9 board valid?

Intuition. For each cell, build three keys: (row, val), (col, val), (box, val). All keys must be unique.

def isValidSudoku(board):
    seen = set()
    for i in range(9):
        for j in range(9):
            v = board[i][j]
            if v == '.': continue
            keys = (f"r{i}{v}", f"c{j}{v}", f"b{i//3}{j//3}{v}")
            if any(k in seen for k in keys): return False
            seen.update(keys)
    return True

Complexity. .

Hook. "Three keys per cell."


9. Longest Consecutive Sequence (LC 128)

Problem. Longest run of consecutive integers (in any order in the array).

Intuition. Put nums in a set. For each n, only start counting if n-1 not in set (so each run starts at its smallest). Each number visited at most twice → amortized.

def longestConsecutive(nums):
    s = set(nums)
    best = 0
    for n in s:
        if n - 1 in s: continue   # not a run start
        cur = n
        while cur + 1 in s: cur += 1
        best = max(best, cur - n + 1)
    return best

Complexity. time and space.

Hook. "Only start at run-starts (n where n-1 not in set)."

Watch out. Without the n-1 skip, you re-walk runs and become .


Sliding Window (6)

10. Best Time to Buy and Sell Stock (LC 121)

Problem. Max profit from one buy + one sell.

Intuition. Track running min price; max profit at each day = price - running_min.

def maxProfit(prices):
    lo = prices[0]
    best = 0
    for p in prices[1:]:
        lo = min(lo, p)
        best = max(best, p - lo)
    return best

Complexity. time, space.

Hook. "Running min, running profit."


11. Longest Substring Without Repeating Characters (LC 3)

Problem. Longest substring with all unique characters.

Intuition. Sliding window. When you see a duplicate, shrink from the left until it's gone.

def lengthOfLongestSubstring(s):
    seen = {}
    l, best = 0, 0
    for r, c in enumerate(s):
        if c in seen and seen[c] >= l:
            l = seen[c] + 1
        seen[c] = r
        best = max(best, r - l + 1)
    return best

Complexity. time, space.

Hook. "Jump l past last duplicate."

Watch out. seen[c] >= l check — old positions before l are stale.


12. Longest Repeating Character Replacement (LC 424)

Problem. Longest substring after replacing at most chars with the same char.

Intuition. Window is valid iff (window length) - (max char count in window) ≤ k. Expand always; shrink when invalid.

from collections import Counter
def characterReplacement(s, k):
    cnt = Counter()
    l, best, max_c = 0, 0, 0
    for r, c in enumerate(s):
        cnt[c] += 1
        max_c = max(max_c, cnt[c])
        if (r - l + 1) - max_c > k:
            cnt[s[l]] -= 1
            l += 1
        best = max(best, r - l + 1)
    return best

Complexity. time, space.

Hook. "Window − max-count ≤ k."

Watch out. max_c doesn't need to be re-validated on shrink — best length only grows when a higher max_c is seen.


13. Permutation in String (LC 567)

Problem. Does s2 contain a permutation of s1 as substring?

Intuition. Fixed-size sliding window of length . Compare character counts.

from collections import Counter
def checkInclusion(s1, s2):
    if len(s1) > len(s2): return False
    need = Counter(s1)
    have = Counter(s2[:len(s1)])
    if need == have: return True
    for r in range(len(s1), len(s2)):
        have[s2[r]] += 1
        have[s2[r - len(s1)]] -= 1
        if have[s2[r - len(s1)]] == 0:
            del have[s2[r - len(s1)]]
        if have == need: return True
    return False

Complexity. time, space (26 letters).

Hook. "Fixed window, counter equality."


14. Minimum Window Substring (LC 76)

Problem. Smallest substring of containing all chars of .

Intuition. Expand right; once all needed chars satisfied, shrink left as far as possible while staying valid; track min.

from collections import Counter
def minWindow(s, t):
    if not t or not s: return ""
    need = Counter(t)
    have = Counter()
    matches = 0       # count of distinct chars satisfied
    target = len(need)
    l, best = 0, (float('inf'), 0, 0)
    for r, c in enumerate(s):
        have[c] += 1
        if c in need and have[c] == need[c]:
            matches += 1
        while matches == target:
            if r - l + 1 < best[0]:
                best = (r - l + 1, l, r)
            have[s[l]] -= 1
            if s[l] in need and have[s[l]] < need[s[l]]:
                matches -= 1
            l += 1
    return "" if best[0] == float('inf') else s[best[1]:best[2] + 1]

Complexity. time.

Hook. "Track distinct-chars-satisfied; expand-then-shrink."

Watch out. Decrement matches only when a need-char drops below its required count.


15. Sliding Window Maximum (LC 239)

Problem. Max in each window of size .

Intuition. Monotonic deque of indices, decreasing values. Front is the max. Pop back if smaller, pop front if out of window.

from collections import deque
def maxSlidingWindow(nums, k):
    q = deque()   # indices, nums[q] decreasing
    out = []
    for i, x in enumerate(nums):
        while q and nums[q[-1]] < x:
            q.pop()
        q.append(i)
        if q[0] <= i - k:
            q.popleft()
        if i >= k - 1:
            out.append(nums[q[0]])
    return out

Complexity. amortized.

Hook. "Monotonic deque; front is max."

Watch out. Pop from back while back is smaller (it can't be the max anymore for any future window).


How to drill these

  1. Read each problem's Hook alone; try to write the solution. If you can't, re-read.
  2. Re-implement on a blank file 24 hours later from the hook only.
  3. Re-attempt 1 week later. Three reps over 30 days = locked in.

Summary table

#ProblemPatternTimeSpaceHook
1Contains Duplicatehash setset vs len
2Valid Anagramcountercounter equal
3Two Sumhash mapseen complement
4Group Anagramshash by canonicalcanonical key
5Top K Frequentbucket sortfreq-as-index
6Encode/Decodelength-prefixlen#str
7Product Except Selfprefix+suffixleft-then-right
8Valid Sudoku3 keys per cellrow/col/box keys
9Longest Consecutiveset + skip non-startsstart where n-1 missing
10Buy/Sell Stockrunning minmin-then-diff
11Longest Unique Substringsliding windowjump l past dup
12Char Replacementwindow-max-cnt ≤ kwindow−maxc≤k
13Permutation in Stringfixed window countercounter eq
14Min Window Substringmatches countermatches==target
15Sliding Window Maxmonotonic dequefront is max