Sudoku Solver Using Backtracking in DSA C++ - Time & Space Complexity
We want to understand how the time needed to solve a Sudoku puzzle changes as the puzzle size or difficulty changes.
Specifically, how does the backtracking method scale when trying to fill the board?
Analyze the time complexity of the following Sudoku solver using backtracking.
bool solveSudoku(int board[9][9]) {
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 and uses recursion to fill the board step-by-step.
Look at the loops and recursion that repeat work.
- Primary operation: Trying numbers 1 to 9 in each empty cell and recursively solving the rest.
- How many times: Up to 9 attempts per empty cell, and recursion explores many board states.
The number of empty cells affects how many guesses and recursive calls happen.
| Input Size (empty cells) | Approx. Operations |
|---|---|
| 10 | Up to 9^10 = 3,486,784,401 tries |
| 20 | Up to 9^20 = 1.21e+19 tries (very large) |
| 40 | Up to 9^40 = 1.21e+38 tries (astronomical) |
Pattern observation: The operations grow exponentially with the number of empty cells, making the problem very costly as empties increase.
Time Complexity: O(9^m) where m is the number of empty cells
This means the time grows very fast as more empty cells need to be filled, because each cell can try up to 9 numbers recursively.
[X] Wrong: "The solver always takes the same time regardless of the puzzle."
[OK] Correct: The time depends heavily on how many empty cells and how constrained the puzzle is; some puzzles solve quickly, others take much longer.
Understanding this exponential growth helps you explain why backtracking can be slow and why pruning or heuristics matter in real problems.
"What if we added a heuristic to pick the next empty cell with the fewest valid options? How would the time complexity change?"
Practice
backtracking technique used in a Sudoku solver?Solution
Step 1: Understand backtracking in Sudoku
Backtracking tries numbers in empty cells and checks if they fit the Sudoku rules.Step 2: Undo if conflict arises
If a number causes a conflict later, it is removed (backtracked) and another number is tried.Final Answer:
Try placing numbers and undo if they lead to conflicts -> Option CQuick Check:
Backtracking = Try and undo [OK]
- Thinking backtracking fills randomly
- Assuming backtracking fills only once without undo
- Believing backtracking uses fixed patterns
Solution
Step 1: Understand the safety check function
The functionisSafechecks if placingnumatboard[row][col]follows Sudoku rules.Step 2: Place number only if safe
Only ifisSafereturns true, the number is placed in the cell.Final Answer:
if (isSafe(board, row, col, num)) { board[row][col] = num; } -> Option AQuick Check:
Use isSafe before placing number [OK]
- Placing number without checking rules
- Checking if cell already has the number incorrectly
- Using invalid conditions for placement
int board[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}
};
solveSudoku(board);
Assuming solveSudoku correctly solves the puzzle, what will be the value at board[0][2] after solving?Solution
Step 1: Identify the empty cell at (0,2)
The cell at row 0, column 2 is initially 0 (empty).Step 2: Solve Sudoku and find correct number
After solving, the number 4 fits at (0,2) without conflicts in row, column, or box.Final Answer:
4 -> Option BQuick Check:
board[0][2] = 4 after solve [OK]
- Guessing without checking Sudoku rules
- Confusing row and column indices
- Assuming initial zeros remain zeros
bool isSafe(int board[9][9], 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[i][j] == num)
return false;
}
}
return true;
}
What is the bug in this code?Solution
Step 1: Analyze 3x3 box checking loop
The inner loop checksboard[i][j]instead ofboard[startRow + i][startCol + j], so it checks wrong cells.Step 2: Correct indices for box check
To check the correct 3x3 box, addstartRowandstartColoffsets toiandj.Final Answer:
The 3x3 box check uses wrong indices; should use startRow + i and startCol + j -> Option AQuick Check:
Box check indices must include startRow/startCol [OK]
- Checking wrong cells in 3x3 box
- Ignoring row or column checks
- Incorrect modulo usage
Solution
Step 1: Understand heuristic to reduce search space
Choosing the cell with the fewest valid options first reduces branching and speeds up solving.Step 2: Compare other strategies
Filling first empty cell or random tries increase backtracking steps; ignoring constraints causes errors.Final Answer:
Choose the empty cell with the fewest possible valid numbers first -> Option DQuick Check:
Minimum remaining values heuristic = choose cell with fewest options [OK]
- Filling cells without checking possibilities
- Trying numbers randomly wastes time
- Ignoring constraints leads to wrong solutions
Ready to prove it?
Pass the test and this pattern moves unknown
