Skip to main content

Sudoku Solver

Problem

Write a program to solve a Sudoku puzzle by filling the empty cells.

A sudoku solution must satisfy all of the following rules:

  1. Each of the digits 1-9 must occur exactly once in each row.
  2. Each of the digits 1-9 must occur exactly once in each column.
  3. Each of the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.

The '.' character indicates empty cells.

 

Example 1:

Input: board = [["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"]]
Output: [["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"]]
Explanation: The input board is shown above and the only valid solution is shown below:


 

Constraints:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] is a digit or '.'.
  • It is guaranteed that the input board has only one solution.

Solution

/**
* @param {character[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
var solveSudoku = function(board) {
backtrack(board, 0, 0);
return board;
};

var backtrack = function(board, r, c) {
if (r === 9) { // all cells on board filled
return true;
}
const nextR = c === 8 ? r + 1 : r; // next row to solve
const nextC = c === 8 ? 0 : c + 1; // next col to solve

if (board[r][c] === ".") { // (r, c) unfilled
const moves = possibleMoves(board, r, c);
for (const m of moves) { // try all possible moves
board[r][c] = m;
if (backtrack(board, nextR, nextC)) {
return true;
}
board[r][c] = ".";
}
return false;
} else { // (r, c) filled
return backtrack(board, nextR, nextC);
}
};

// get array of possible moves at (r, c)
var possibleMoves = function(board, r, c) {
const map = Array(9).fill(false);

// row and col
for (let i = 0; i < 9; i++) {
if (board[r][i] !== ".") {
map[parseInt(board[r][i]) - 1] = true;
}
if (board[i][c] !== ".") {
map[parseInt(board[i][c]) - 1] = true;
}
}

// sub-box
const subBoxR = Math.floor(r / 3) * 3; // top row of sub-box
const subBoxC = Math.floor(c / 3) * 3; // left col of sub-box

for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (board[i + subBoxR][j + subBoxC] !== ".") {
map[parseInt(board[i + subBoxR][j + subBoxC]) - 1] = true;
}
}
}

// return values [1, 9] that were not encountered
return map.reduce((acc, cur, index) => {
if (!cur) {
acc.push(`${index + 1}`);
}
return acc;
}, []);
};

We will implement a backtracking (DFS) solution. We use the method possibleMoves to obtain an array of valid moves at (r, c) based on the current condition of board. Using this, we attempt to fill in all empty cells of board (going from left to right, top to bottom) by trying all possible valid moves from this array until either there are no more possible moves for the next (r, c), or the board is filled.