Quick Info

Category: Backtracking
Time Complexity: O(9^(n*n))
Space Complexity: O(n*n)
Input Type: 9x9 Sudoku Board

Sudoku Solver

Description

The Sudoku Solver algorithm uses backtracking to solve a 9x9 Sudoku puzzle. The goal is to fill the grid with numbers from 1 to 9, ensuring that each number appears exactly once in each row, column, and 3x3 sub-grid.

How It Works

  1. Start with an incomplete Sudoku grid
  2. Find an empty cell (represented by 0)
  3. Try placing numbers 1-9 in the empty cell
  4. For each number, check if it's valid in the current position:
    • Check if the number exists in the current row
    • Check if the number exists in the current column
    • Check if the number exists in the current 3x3 box
  5. If the number is valid, place it and recursively try to solve the rest of the puzzle
  6. If the recursive call fails, backtrack by removing the number and try the next one
  7. If no number works, return false to trigger backtracking
  8. If all cells are filled, the puzzle is solved

Visualization

Enter numbers and click Start to solve

Implementation


def is_valid(board, row, col, num):
    # Check row
    for x in range(9):
        if board[row][x] == num:
            return False
    
    # Check column
    for x in range(9):
        if board[x][col] == num:
            return False
    
    # Check 3x3 box
    start_row = row - row % 3
    start_col = col - col % 3
    for i in range(3):
        for j in range(3):
            if board[i + start_row][j + start_col] == num:
                return False
    
    return True

def solve_sudoku(board):
    empty = find_empty(board)
    if not empty:
        return True
    
    row, col = empty
    
    for num in range(1, 10):
        if is_valid(board, row, col, num):
            board[row][col] = num
            
            if solve_sudoku(board):
                return True
            
            board[row][col] = 0
    
    return False

def find_empty(board):
    for i in range(9):
        for j in range(9):
            if board[i][j] == 0:
                return (i, j)
    return None
                                    

class SudokuSolver {
private:
    bool isValid(vector<vector<int>>& board, int row, int col, int num) {
        // Check row
        for (int x = 0; x < 9; x++) {
            if (board[row][x] == num) return false;
        }
        
        // Check column
        for (int x = 0; x < 9; x++) {
            if (board[x][col] == num) return false;
        }
        
        // Check 3x3 box
        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 + startRow][j + startCol] == num) return false;
            }
        }
        
        return true;
    }
    
    pair<int, int> findEmpty(vector<vector<int>>& board) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == 0) {
                    return {i, j};
                }
            }
        }
        return {-1, -1};
    }
    
public:
    bool solveSudoku(vector<vector<int>>& board) {
        auto empty = findEmpty(board);
        if (empty.first == -1) return true;
        
        int row = empty.first;
        int col = empty.second;
        
        for (int num = 1; num <= 9; num++) {
            if (isValid(board, row, col, num)) {
                board[row][col] = num;
                
                if (solveSudoku(board)) {
                    return true;
                }
                
                board[row][col] = 0;
            }
        }
        
        return false;
    }
};
                                    

public class SudokuSolver {
    private bool IsValid(int[,] board, int row, int col, int num) {
        // Check row
        for (int x = 0; x < 9; x++) {
            if (board[row, x] == num) return false;
        }
        
        // Check column
        for (int x = 0; x < 9; x++) {
            if (board[x, col] == num) return false;
        }
        
        // Check 3x3 box
        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 + startRow, j + startCol] == num) return false;
            }
        }
        
        return true;
    }
    
    private (int row, int col) FindEmpty(int[,] board) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i, j] == 0) {
                    return (i, j);
                }
            }
        }
        return (-1, -1);
    }
    
    public bool SolveSudoku(int[,] board) {
        var empty = FindEmpty(board);
        if (empty.row == -1) return true;
        
        int row = empty.row;
        int col = empty.col;
        
        for (int num = 1; num <= 9; num++) {
            if (IsValid(board, row, col, num)) {
                board[row, col] = num;
                
                if (SolveSudoku(board)) {
                    return true;
                }
                
                board[row, col] = 0;
            }
        }
        
        return false;
    }
}
                                    

Complexity Analysis

Time Complexity

The time complexity of the Sudoku solver is O(9^(n*n)), where n is the size of the board (typically 9). This is because:

  • For each empty cell, we try numbers 1-9
  • There can be up to n*n empty cells
  • For each attempt, we need to validate the move (O(1))
  • The algorithm backtracks when invalid solutions are found

Space Complexity

The space complexity is O(n*n), where n is the board size. This accounts for:

  • The recursion stack depth (up to n*n calls)
  • The board representation itself
  • No additional significant space requirements

Advantages and Disadvantages

Advantages

  • Guaranteed to find a solution if one exists
  • Simple to implement and understand
  • Memory efficient
  • Can solve any valid Sudoku puzzle
  • No pre-processing required

Disadvantages

  • Exponential time complexity
  • Can be slow for very difficult puzzles
  • Not optimal for puzzles with multiple solutions
  • Performance depends heavily on initial board state
  • May explore many dead ends before finding solution