Bird
Raised Fist0
DSA Typescriptprogramming

Sudoku Solver Using Backtracking in DSA Typescript

Choose your learning style10 modes available
Mental Model
Try placing numbers one by one in empty cells and backtrack if a number leads to a conflict.
Analogy: Like trying keys on a locked door one by one, and if a key doesn't fit, you go back and try another key until the door opens.
5 3 _ | _ 7 _ | _ _ _
6 _ _ | 1 9 5 | _ _ _
_ 9 8 | _ _ _ | _ 6 _
------+-------+------
8 _ _ | _ 6 _ | _ _ 3
4 _ _ | 8 _ 3 | _ _ 1
7 _ _ | _ 2 _ | _ _ 6
------+-------+------
_ 6 _ | _ _ _ | 2 8 _
_ _ _ | 4 1 9 | _ _ 5
_ _ _ | _ 8 _ | _ 7 9
↑ empty cells marked as _
Dry Run Walkthrough
Input: Partially filled 9x9 Sudoku board with some empty cells marked as 0
Goal: Fill all empty cells with numbers 1-9 so that each row, column, and 3x3 box contains all digits without repetition
Step 1: Find first empty cell at row 0, column 2 and try number 1
5 3 [1] | _ 7 _ | _ _ _
6 _ _ | 1 9 5 | _ _ _
_ 9 8 | _ _ _ | _ 6 _
------+-------+------
8 _ _ | _ 6 _ | _ _ 3
4 _ _ | 8 _ 3 | _ _ 1
7 _ _ | _ 2 _ | _ _ 6
------+-------+------
_ 6 _ | _ _ _ | 2 8 _
_ _ _ | 4 1 9 | _ _ 5
_ _ _ | _ 8 _ | _ 7 9
Why: Try smallest number first to find a valid placement
Step 2: Check if number 1 is valid at row 0, column 2; it conflicts with number 1 in column 3, so try next number 2
5 3 [2] | _ 7 _ | _ _ _
6 _ _ | 1 9 5 | _ _ _
_ 9 8 | _ _ _ | _ 6 _
------+-------+------
8 _ _ | _ 6 _ | _ _ 3
4 _ _ | 8 _ 3 | _ _ 1
7 _ _ | _ 2 _ | _ _ 6
------+-------+------
_ 6 _ | _ _ _ | 2 8 _
_ _ _ | 4 1 9 | _ _ 5
_ _ _ | _ 8 _ | _ 7 9
Why: Number 1 conflicts, so try next number
Step 3: Number 2 is valid at row 0, column 2; move to next empty cell at row 0, column 3
5 3 2 | [_] 7 _ | _ _ _
6 _ _ | 1 9 5 | _ _ _
_ 9 8 | _ _ _ | _ 6 _
------+-------+------
8 _ _ | _ 6 _ | _ _ 3
4 _ _ | 8 _ 3 | _ _ 1
7 _ _ | _ 2 _ | _ _ 6
------+-------+------
_ 6 _ | _ _ _ | 2 8 _
_ _ _ | 4 1 9 | _ _ 5
_ _ _ | _ 8 _ | _ 7 9
Why: Placed valid number, proceed to next empty cell
Step 4: Try numbers 1 to 9 at row 0, column 3; numbers 1, 2, 3 conflict; number 4 is valid
5 3 2 | [4] 7 _ | _ _ _
6 _ _ | 1 9 5 | _ _ _
_ 9 8 | _ _ _ | _ 6 _
------+-------+------
8 _ _ | _ 6 _ | _ _ 3
4 _ _ | 8 _ 3 | _ _ 1
7 _ _ | _ 2 _ | _ _ 6
------+-------+------
_ 6 _ | _ _ _ | 2 8 _
_ _ _ | 4 1 9 | _ _ 5
_ _ _ | _ 8 _ | _ 7 9
Why: Find first valid number for this cell
Step 5: Continue this process recursively, backtracking when no valid number found for a cell
Fully filled Sudoku board with no conflicts:
5 3 4 | 6 7 8 | 9 1 2
6 7 2 | 1 9 5 | 3 4 8
1 9 8 | 3 4 2 | 5 6 7
------+-------+------
8 5 9 | 7 6 1 | 4 2 3
4 2 6 | 8 5 3 | 7 9 1
7 1 3 | 9 2 4 | 8 5 6
------+-------+------
9 6 1 | 5 3 7 | 2 8 4
2 8 7 | 4 1 9 | 6 3 5
3 4 5 | 2 8 6 | 1 7 9
Why: Backtracking ensures all possibilities are explored until solution is found
Result:
Fully solved Sudoku board:
5 3 4 | 6 7 8 | 9 1 2
6 7 2 | 1 9 5 | 3 4 8
1 9 8 | 3 4 2 | 5 6 7
------+-------+------
8 5 9 | 7 6 1 | 4 2 3
4 2 6 | 8 5 3 | 7 9 1
7 1 3 | 9 2 4 | 8 5 6
------+-------+------
9 6 1 | 5 3 7 | 2 8 4
2 8 7 | 4 1 9 | 6 3 5
3 4 5 | 2 8 6 | 1 7 9
Annotated Code
DSA Typescript
class SudokuSolver {
  board: number[][];

  constructor(board: number[][]) {
    this.board = board;
  }

  isValid(row: number, col: number, num: number): boolean {
    // Check row for duplicate
    for (let x = 0; x < 9; x++) {
      if (this.board[row][x] === num) return false;
    }

    // Check column for duplicate
    for (let y = 0; y < 9; y++) {
      if (this.board[y][col] === num) return false;
    }

    // Check 3x3 box for duplicate
    const startRow = Math.floor(row / 3) * 3;
    const startCol = Math.floor(col / 3) * 3;
    for (let i = 0; i < 3; i++) {
      for (let j = 0; j < 3; j++) {
        if (this.board[startRow + i][startCol + j] === num) return false;
      }
    }

    return true;
  }

  solve(): boolean {
    for (let row = 0; row < 9; row++) {
      for (let col = 0; col < 9; col++) {
        if (this.board[row][col] === 0) {
          for (let num = 1; num <= 9; num++) {
            if (this.isValid(row, col, num)) {
              this.board[row][col] = num; // place num

              if (this.solve()) {
                return true; // solved
              }

              this.board[row][col] = 0; // backtrack
            }
          }
          return false; // no valid number found
        }
      }
    }
    return true; // no empty cells left
  }

  printBoard(): void {
    for (let i = 0; i < 9; i++) {
      let line = '';
      for (let j = 0; j < 9; j++) {
        line += this.board[i][j] + (j === 8 ? '' : ' ');
      }
      console.log(line);
    }
  }
}

const board = [
  [5,3,0,0,7,0,0,0,0],
  [6,0,0,1,9,5,0,0,0],
  [0,9,8,0,0,0,0,6,0],
  [8,0,0,0,6,0,0,0,3],
  [4,0,0,8,0,3,0,0,1],
  [7,0,0,0,2,0,0,0,6],
  [0,6,0,0,0,0,2,8,0],
  [0,0,0,4,1,9,0,0,5],
  [0,0,0,0,8,0,0,7,9]
];

const solver = new SudokuSolver(board);
if (solver.solve()) {
  solver.printBoard();
} else {
  console.log('No solution exists');
}
for (let x = 0; x < 9; x++) { if (this.board[row][x] === num) return false; }
Check row for duplicate number
for (let y = 0; y < 9; y++) { if (this.board[y][col] === num) return false; }
Check column for duplicate number
for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (this.board[startRow + i][startCol + j] === num) return false; } }
Check 3x3 box for duplicate number
if (this.board[row][col] === 0) { for (let num = 1; num <= 9; num++) { if (this.isValid(row, col, num)) { this.board[row][col] = num;
Try numbers 1-9 in empty cell and place if valid
if (this.solve()) { return true; }
Recursively solve next cells; if solved, return true
this.board[row][col] = 0;
Backtrack by resetting cell if no number leads to solution
return false;
Return false if no valid number found for current cell
OutputSuccess
5 3 4 6 7 8 9 1 2 6 7 2 1 9 5 3 4 8 1 9 8 3 4 2 5 6 7 8 5 9 7 6 1 4 2 3 4 2 6 8 5 3 7 9 1 7 1 3 9 2 4 8 5 6 9 6 1 5 3 7 2 8 4 2 8 7 4 1 9 6 3 5 3 4 5 2 8 6 1 7 9
Complexity Analysis
Time: O(9^(n)) where n is number of empty cells, because each empty cell tries up to 9 numbers recursively
Space: O(n) due to recursion stack for n empty cells
vs Alternative: Backtracking is more efficient than brute force trying all board configurations because it prunes invalid paths early
Edge Cases
Empty board (all zeros)
Solver tries all possibilities, may take longer but still finds solution or reports none
DSA Typescript
if (this.board[row][col] === 0) {
Already solved board
Solver quickly returns true without changes
DSA Typescript
return true; // no empty cells left
Board with no valid solution
Solver backtracks fully and returns false
DSA Typescript
return false; // no valid number found
When to Use This Pattern
When you see a problem requiring filling a grid with constraints and trial/error, reach for backtracking because it tries options and undoes wrong choices systematically.
Common Mistakes
Mistake: Not checking all constraints (row, column, box) before placing a number
Fix: Implement isValid function to check all three constraints before placing
Mistake: Not resetting cell to zero when backtracking
Fix: Set cell back to zero after recursive call fails to undo placement
Mistake: Stopping after first empty cell without trying all numbers
Fix: Try all numbers 1 to 9 in each empty cell before backtracking
Summary
It fills empty Sudoku cells by trying numbers and backtracking on conflicts.
Use it when you need to solve puzzles with constraints and multiple choices.
The key insight is to try a choice, move forward, and undo if it leads to a dead end.

Practice

(1/5)
1. What is the main idea behind using backtracking to solve a Sudoku puzzle?
easy
A. Use a sorting algorithm to arrange numbers in each row
B. Try placing numbers one by one and backtrack if a conflict occurs
C. Fill all empty cells randomly and check if the puzzle is solved
D. Replace all zeros with the number 9 directly

Solution

  1. Step 1: Understand backtracking approach

    Backtracking tries numbers in empty cells one by one and checks if they fit the Sudoku rules.
  2. Step 2: Handle conflicts by undoing choices

    If a number causes a conflict later, backtracking removes it and tries the next number.
  3. Final Answer:

    Try placing numbers one by one and backtrack if a conflict occurs -> Option B
  4. Quick Check:

    Backtracking = Try and undo choices [OK]
Hint: Backtracking means try, check, undo if needed [OK]
Common Mistakes:
  • Thinking backtracking fills cells randomly
  • Assuming sorting solves Sudoku
  • Replacing zeros without checks
2. Which of the following TypeScript function signatures correctly represents a function to check if placing a number is valid in Sudoku?
easy
A. function isValid(board: number[][], row: number, col: number, num: number): boolean
B. function isValid(board: number[], row: number, col: number): number
C. function isValid(board: string[][], num: string): void
D. function isValid(board: number[][], num: number): string

Solution

  1. Step 1: Analyze function parameters

    The function needs the board (2D array), row, column, and number to check validity.
  2. Step 2: Check return type

    It should return a boolean indicating if placing the number is valid or not.
  3. Final Answer:

    function isValid(board: number[][], row: number, col: number, num: number): boolean -> Option A
  4. Quick Check:

    Correct parameters and boolean return [OK]
Hint: Check function inputs and boolean output for validity [OK]
Common Mistakes:
  • Using wrong parameter types
  • Missing row or column parameters
  • Returning wrong data type
3. Given the following partial Sudoku board and the backtracking function call solveSudoku(board), what will be the value at board[0][2] after the first successful placement?
const board = [
  [5, 3, 0, 0, 7, 0, 0, 0, 0],
  [6, 0, 0, 1, 9, 5, 0, 0, 0],
  [0, 9, 8, 0, 0, 0, 0, 6, 0],
  [8, 0, 0, 0, 6, 0, 0, 0, 3],
  [4, 0, 0, 8, 0, 3, 0, 0, 1],
  [7, 0, 0, 0, 2, 0, 0, 0, 6],
  [0, 6, 0, 0, 0, 0, 2, 8, 0],
  [0, 0, 0, 4, 1, 9, 0, 0, 5],
  [0, 0, 0, 0, 8, 0, 0, 7, 9]
];
medium
A. 4
B. 2
C. 1
D. 7

Solution

  1. Step 1: Identify empty cell and possible numbers

    At position (0,2), possible numbers are those not in row 0, column 2, or 3x3 box. Candidates: 1, 2, 4.
  2. Step 2: Check numbers 1 to 9 for validity

    1, 2, 4 are initially valid. Backtracking tries 1 first, recurses but backtracks due to later conflicts; tries 2, same; 4 succeeds and solves the puzzle.
  3. Final Answer:

    4 -> Option A
  4. Quick Check:

    First successful number placed = 4 [OK]
Hint: Check row, column, box for smallest valid number [OK]
Common Mistakes:
  • Choosing a number already in the same column
  • Ignoring 3x3 box constraints
  • Assuming first empty cell is filled with 1 always
4. Identify the bug in this TypeScript snippet for checking if a number can be placed in a Sudoku cell:
function isValid(board: number[][], row: number, col: number, num: number): boolean {
  for (let i = 0; i < 9; i++) {
    if (board[row][i] === num) return false;
    if (board[i][col] === num) return false;
  }
  const startRow = row - row % 3;
  const startCol = col - col % 3;
  for (let i = startRow; i < startRow + 3; i++) {
    for (let j = startCol; j < startCol + 3; j++) {
      if (board[i][j] === num) return false;
    }
  }
  return true;
}
medium
A. The function does not check the entire row correctly
B. The calculation of startRow and startCol is incorrect
C. The function checks the row and column twice causing errors
D. No bug; the function correctly checks validity

Solution

  1. Step 1: Review row and column checks

    The loop checks all cells in the given row and column for the number.
  2. Step 2: Review 3x3 box check

    startRow and startCol correctly find the top-left cell of the 3x3 box; nested loops check all 9 cells.
  3. Final Answer:

    No bug; the function correctly checks validity -> Option D
  4. Quick Check:

    All constraints checked properly [OK]
Hint: Check row, column, and box loops carefully [OK]
Common Mistakes:
  • Miscomputing startRow or startCol
  • Forgetting to check 3x3 box
  • Returning false too early
5. You want to optimize the Sudoku solver by stopping early when the puzzle is solved. Which modification to the backtracking function is best to achieve this?
hard
A. Only check rows and columns, skip 3x3 box checks
B. Continue trying all numbers in all cells even after solution found
C. Return true immediately when a valid number is placed in the last empty cell
D. Remove the base case and let recursion run fully

Solution

  1. Step 1: Understand base case in backtracking

    The base case returns true when all cells are filled correctly, stopping recursion.
  2. Step 2: Modify to return true immediately on last cell

    Returning true immediately when the last empty cell is filled avoids unnecessary further checks.
  3. Final Answer:

    Return true immediately when a valid number is placed in the last empty cell -> Option C
  4. Quick Check:

    Stop recursion early by returning true [OK]
Hint: Return true at last cell to stop recursion early [OK]
Common Mistakes:
  • Ignoring base case and continuing recursion
  • Skipping box checks causing wrong solutions
  • Trying all numbers even after solution
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard