๐ฌ Saturday Deep Dive: Valid Sudoku (15 min read)
๐ Day 8/150 ยท NeetCode: 8/150 ยท SysDesign: 8/40 ยท Behavioral: 8/40 ยท Frontend: 8/50 ยท AI: 4/30
๐ฅ 8-day streak!
๐ LeetCode #36: Valid Sudoku ๐ก Medium
๐น NeetCode Solution
Overview / ๆฆ่ฟฐ
ไปๅคฉๆไปฌๆทฑๅ ฅ็ ็ฉถไธ้็ปๅ ธ็ไธญ็ญ้พๅบฆ้ข๏ผ้ช่ฏๆฐ็ฌ็้ข็ๅๆณๆงใ
Today we deep-dive into a classic Medium problem: checking whether a Sudoku board is valid.
่ฟ้้ข็่ตทๆฅ็ฎๅ๏ผๅฎๅ่ๅฏไฝ ๅฏนๅค็ปดๅๅธใๅๆ ๆ ๅฐๅ่พน็ๆกไปถ็ๆๆกใ
It looks approachable, but tests your grasp of multi-dimensional hashing, coordinate mapping, and edge conditions.
้ข่ฏไธญๅฎ้ข็นๅบ็ฐ๏ผๅ ไธบๅฎ่ๅฏ็ไธๆฏ"ไฝ ่ฝไธ่ฝๅๅพช็ฏ"๏ผ่ๆฏ"ไฝ ่ฝไธ่ฝ็ณป็ปๅฐๅฏนๆฐๆฎๅปบๆจก"ใ
It appears often in interviews not to test loop-writing, but to test systematic data modeling.
Part 1: Theory / ็่ฎบๅบ็ก (5 min)
ไปไนๆฏๆฐ็ฌ้ช่ฏ๏ผ/ What Are We Validating?
ไธไธชๅๆณ็ๆฐ็ฌ็้ขๆปก่ถณไธไธช็บฆๆ๏ผ
A valid Sudoku board satisfies exactly three constraints:
ๅ ณ้ฎ็น๏ผๆไปฌไธ้่ฆ้ช่ฏๆฐ็ฌ่ฝๅฆ่ขซ่งฃๅบ๏ผๅช้่ฆ้ช่ฏๅฝๅๅทฒๅกซๆฐๅญๆฏๅฆๅๆณใ
Key insight: We don't need to verify the puzzle is solvable โ only that existing digits don't violate rules.
'.' ไปฃ่กจ็ฉบๆ ผ๏ผๅฟฝ็ฅไธ่ฎกใ/ '.' means empty cell โ skip it.
ๆฐๆฎๅปบๆจก๏ผๆ ธๅฟๆๆณ / Data Modeling: The Core Idea
ๆ็ด่ง็ๆดๅ่งฃๆณๆฏ๏ผๅๅซๅฏนๆฏ่กใๆฏๅใๆฏไธชๅฎซๆ ผๅ้ช่ฏ๏ผๅตๅฅๅพช็ฏไนๆฌกใ
The naive approach: check rows, columns, and boxes separately โ nine nested loops.
ๆดไผ้ ็ๆนๅผ๏ผไธๆฌก้ๅ๏ผๅๆถ็ปดๆคไธไธช้ๅๅญๅ ธใ
Better: one pass, three sets of dictionaries updated simultaneously.
่ฟไธช (r // 3, c // 3) ๆฏๆฌ้ขๆไผ้
็ๆๅทง๏ผๅฐๅๆ ๆ ๅฐไธบ0-8็็ๅญ็ดขๅผใ
The (r // 3, c // 3) trick maps any cell to its 3ร3 box index โ this is the elegance of the problem.
Part 2: Step-by-Step Implementation / ไธๆญฅไธๆญฅๅฎ็ฐ (8 min)
Approach 1: Naive (brute force) โ O(9ยฒ) time but ugly
้ช่ฏๆฏ่กใๆฏๅใๆฏๅฎซๆ ผ้่ฆ9ๆฌก็ฌ็ซๅพช็ฏใๅฏ่ฏปๆงๅทฎ๏ผๅฎนๆๅบ้ใ
Nine separate loops for rows, columns, boxes. Hard to read, error-prone.
# Approach 1: Naive โ DO NOT use in interview (too verbose)
def isValidSudoku_naive(board):
# Check rows
for row in board:
seen = set()
for c in row:
if c == '.': continue
if c in seen: return False
seen.add(c)
# Check cols (transpose and repeat)
for col in range(9):
seen = set()
for row in range(9):
c = board[row][col]
if c == '.': continue
if c in seen: return False
seen.add(c)
# Check 3x3 boxes
for box_row in range(3):
for box_col in range(3):
seen = set()
for r in range(box_row*3, box_row*3+3):
for c in range(box_col*3, box_col*3+3):
val = board[r][c]
if val == '.': continue
if val in seen: return False
seen.add(val)
return True
# Time: O(81) = O(1) technically since board is fixed 9x9
# Space: O(9) per iteration
# Problem: 3 separate passes, harder to extend, verbose
Approach 2: Optimal โ Single Pass, Three Dicts โ
่ฟๆฏ้ข่ฏไธญๅบ่ฏฅ็ปๅบ็็ญๆกใ / This is the answer you should give in an interview.
from collections import defaultdict
def isValidSudoku(board):
# Three dictionaries: rows, columns, boxes
# Each maps an index to a set of seen digits
rows = defaultdict(set) # rows[r] = {digits seen in row r}
cols = defaultdict(set) # cols[c] = {digits seen in col c}
boxes = defaultdict(set) # boxes[(r//3, c//3)] = {digits seen in box}
for r in range(9):
for c in range(9):
val = board[r][c]
# Skip empty cells
if val == '.':
continue
# Calculate box index โ THE KEY TRICK
box_key = (r // 3, c // 3)
# Check all three constraints simultaneously
if (val in rows[r] or
val in cols[c] or
val in boxes[box_key]):
return False # Duplicate found โ invalid!
# Add to all three tracking sets
rows[r].add(val)
cols[c].add(val)
boxes[box_key].add(val)
return True # No violations found
Visual Trace / ้ๆญฅ่ฟฝ่ธช
Let's trace through a small part of the board:
Board (first 3 rows shown):
["5","3",".", ".","7",".", ".",".","."]
["6",".", ".", "1","9","5", ".",".","."]
[".","9","8", ".",".", ".", ".","6","."]
Step-by-step trace:
Part 3: Edge Cases & Gotchas / ่พน็ๆ ๅต (2 min)
Edge Case 1: Empty Board
board = [['.']*9 for _ in range(9)]
# Result: True โ empty board is valid
# Our code handles this: all '.' cells are skipped
Edge Case 2: Same digit in same box, different row AND column
# Trap: this is invalid even though row 0 and col 3 look different
# '5' at (0,0) and '5' at (2,2) โ same 3ร3 box!
# The box_key trick catches this: both map to (0,0)
Edge Case 3: The "it looks valid row-wise" trap
# Each row and column could be valid individually,
# but a 3ร3 box might have duplicates
# Always check all THREE constraints โ don't shortcut!
ๅธธ่ง้ข่ฏๅ / Common Interview Traps
Part 4: Real-World Application / ๅฎ้ ๅบ็จ (2 min)
ๆฐ็ฌ้ช่ฏๅจไธๅชๆฏไธ้้ข่ฏ้ขโโๅฎ็ๅบๅฑๆ่ทฏๅจๅพๅคๅฐๆน้ฝๆๅบ็จใ
Valid Sudoku isn't just an interview puzzle โ its core pattern appears everywhere.
Pattern: Multi-Key Set Membership Check
1. Database Uniqueness Constraints
In databases, a UNIQUE constraint across multiple columns is the same idea:
UNIQUE(user_id, date) โ same as "no two rows share both values"
PostgreSQL internally uses hash indexes (like our sets) per constraint.
2. Spreadsheet Validation
Google Sheets "no duplicates in range" validation:
Checks each row, column, named range simultaneously
Exact same three-constraint structure
3. Compiler Symbol Tables
4. Form Validation in UI
// Same pattern: check uniqueness across multiple dimensions
const seen = { byEmail: new Set(), byUsername: new Set() };
users.forEach(user => {
if (seen.byEmail.has(user.email)) throw Error("duplicate email");
seen.byEmail.add(user.email);
});
Part 5: Interview Simulation / ้ข่ฏๆจกๆ (3 min)
ๅ่ฎพไฝ ๅทฒ็ป็ปๅบไบๆไผ่งฃใ้ข่ฏๅฎๅฏ่ฝไผ้ฎ่ฟไบ้ฎ้ข๏ผ
Assume you've presented the optimal solution. Here are follow-up questions interviewers typically ask:
Q1: "What's the time and space complexity?"
Q2: "Could you do this without any extra space?"
ไธๅฎๅ
จ่ฝ๏ผไฝๅฏไปฅๅ็ผฉใ/ Not quite, but you can compress.
You could encode sets as bitmasks (bit 1 = digit 1 seen, bit 2 = digit 2, etc.):
rows = [0] * 9 # 9 integers, each bit i = digit i+1 seen
cols = [0] * 9
boxes = [0] * 9
For digit d (1-9), check: if rows[r] & (1 << d): return False
rows[r] |= (1 << d)
Space: O(27 integers) = O(1). Faster in practice due to cache locality.
Q3: "How would you extend this to validate a 16ร16 Sudoku (Hexadoku)?"
# Same code, parameterized:
def isValidSudokuNxN(board, n=9, box_size=3):
rows = defaultdict(set)
cols = defaultdict(set)
boxes = defaultdict(set)
for r in range(n):
for c in range(n):
val = board[r][c]
if val == '.': continue
box_key = (r // box_size, c // box_size)
if val in rows[r] or val in cols[c] or val in boxes[box_key]:
return False
rows[r].add(val); cols[c].add(val); boxes[box_key].add(val)
return True
# For 16ร16: n=16, box_size=4 โ same logic, bigger constants
Q4: "What if you needed to not just validate but also solve the Sudoku?"
้ช่ฏๆฏๆฑ่งฃ็ๅญ้ฎ้ขใ/ Validation is a sub-problem of solving.
Solving requires backtracking:
1. Find an empty cell
2. Try digits 1-9
3. For each digit: call isValid (our function!) to check constraints
4. If valid, place digit, recurse
5. If recursion fails, backtrack (remove digit, try next)
Time: O(9^m) where m = number of empty cells โ exponential worst case
Typical Sudoku (m~50 empty cells) solves in milliseconds due to pruning.
Q5: "If this were a production system checking millions of Sudoku boards per second, what would you optimize?"
Complexity Summary / ๅคๆๅบฆๆป็ป
๐ References / ๆทฑๅ ฅๅญฆไน
โข ๐ LeetCode #36: Valid Sudoku ๐ก Medium
โข ๐น NeetCode Solution Video
โข ๐ NeetCode Arrays & Hashing Roadmap โ Visual learning path for this pattern
โข ๐ LeetCode #37: Sudoku Solver โ Natural follow-up (backtracking)
โข ๐ LeetCode #187: Repeated DNA Sequences โ Same multi-key hashing pattern
๐ง ELI5: Checking if a Sudoku is valid is like making sure no two kids in the same row, column, or team have the same number on their shirt โ you write down each number as you see it, and yell "STOP!" if you see it twice in the same group.
Saturday Deep Dive โ Day 8 ยท Generated 2026-03-21