Sudoku Solver Using Backtracking in DSA C# - Time & Space Complexity
We want to understand how the time needed to solve a Sudoku puzzle grows as the puzzle size or difficulty changes.
Specifically, how the backtracking method's steps increase with more empty cells.
Analyze the time complexity of the following Sudoku solver using backtracking.
bool SolveSudoku(int[,] board) {
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
if (board[row, col] == 0) {
for (int num = 1; num <= 9; num++) {
if (IsSafe(board, row, col, num)) {
board[row, col] = num;
if (SolveSudoku(board)) return true;
board[row, col] = 0;
}
}
return false;
}
}
}
return true;
}
This code tries numbers 1 to 9 in empty cells, checking if placing a number is safe, and uses recursion to fill the board.
Look at what repeats most in this code.
- Primary operation: Trying numbers 1 to 9 in each empty cell and checking if safe.
- How many times: For each empty cell, up to 9 attempts, and recursive calls for next cells.
The number of empty cells affects how many guesses and backtracks happen.
| Empty Cells (n) | Approx. Operations |
|---|---|
| 10 | Up to 9^10 (about 3.5 billion) tries in worst case |
| 20 | Up to 9^20 (very large, trillions of trillions) |
| 30 | Up to 9^30 (astronomically large) |
Pattern observation: The operations grow exponentially with the number of empty cells.
Time Complexity: O(9^n)
This means the time needed can grow very fast as the number of empty cells increases, because each cell can try up to 9 numbers recursively.
[X] Wrong: "The solver always tries exactly 9 numbers per empty cell."
[OK] Correct: Actually, many numbers are quickly ruled out by the safety check, so fewer tries happen in practice, making the solver faster than the worst case.
Understanding this exponential growth helps you explain why backtracking is powerful but can be slow, and shows your grasp of recursive problem solving.
"What if we changed the board size from 9x9 to 16x16? How would the time complexity change?"
Practice
Solution
Step 1: Understand backtracking role
Backtracking tries numbers in empty cells and removes them if they cause conflicts.Step 2: Compare other options
Options A, B, and C do not describe backtracking's trial and error approach.Final Answer:
To try possible numbers and undo choices if they lead to conflicts -> Option AQuick Check:
Backtracking = Try and undo choices [OK]
- Thinking backtracking fills randomly without checks
- Confusing backtracking with sorting or counting
- Ignoring the undo step in backtracking
Solution
Step 1: Check method signature correctness
public bool SolveSudoku(int[,] board) uses correct C# syntax: public access, bool return, 2D array parameter.Step 2: Identify errors in other options
bool SolveSudoku(int[][] board) uses wrong array syntax for C#, C uses void return instead of bool, D has semicolon ending which is invalid for method definition.Final Answer:
public bool SolveSudoku(int[,] board) -> Option AQuick Check:
Correct C# method header = public bool SolveSudoku(int[,] board) [OK]
- Using jagged array syntax int[][] instead of int[,]
- Missing public keyword or wrong return type
- Ending method header with semicolon
int[,] board = new int[9,9] {
{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}
};
SudokuSolver solver = new SudokuSolver();
bool solved = solver.SolveSudoku(board);
Console.WriteLine(solved);Solution
Step 1: Understand the input board
The board is a valid partially filled Sudoku puzzle with zeros as empty cells.Step 2: Analyze SolveSudoku method behavior
Assuming correct backtracking implementation, the solver will find a solution and return true.Final Answer:
true -> Option BQuick Check:
Valid puzzle solved = true [OK]
- Assuming zeros cause errors instead of empty cells
- Thinking solver returns false without trying all options
- Confusing compilation/runtime errors with logic
bool IsSafe(int[,] board, int row, int col, int num) {
for (int x = 0; x < 9; x++) {
if (board[row, x] == num || board[x, col] == num) return false;
}
int startRow = row - row % 3;
int startCol = col - col % 3;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[startRow + i, startCol + j] == num) return false;
}
}
return true;
}Solution
Step 1: Review row and column checks
The method checks all cells in the row and column for the number, which is correct.Step 2: Review 3x3 box check
Calculates startRow and startCol correctly using modulo, then checks all 3x3 cells.Step 3: Confirm no missing checks
Method returns true only if no conflicts found, so it is correct.Final Answer:
No bug; the method correctly checks safety -> Option CQuick Check:
Safety check correct = No bug; the method correctly checks safety [OK]
- Miscomputing start indices for 3x3 box
- Forgetting to check both row and column
- Assuming cell fill check is needed here
Solution
Step 1: Understand backtracking efficiency
Choosing the cell with fewest valid options reduces branching and speeds solving.Step 2: Evaluate other options
Random filling (A) wastes time, trying numbers blindly (B) is inefficient, skipping box checks (D) breaks rules.Final Answer:
Choose the empty cell with the fewest possible valid numbers first -> Option DQuick Check:
Minimum options first = faster solving [OK]
- Trying numbers in fixed order without heuristics
- Ignoring 3x3 box constraints
- Randomly filling cells hoping for quick solve
Ready to prove it?
Pass the test and this pattern moves unknown
