Skip to content

Commit 9cc1af8

Browse files
committed
Add Sudoku Solver using Backtracking algorithm- Implements depth-first backtracking to solve 9×9 Sudoku puzzles- Includes validation for rows, columns, and 3×3 subgrids- Provides clean, modular implementation with helper methods- Includes comprehensive unit tests for solvable and unsolvable cases- Time Complexity: O(9^(n²)) in worst case- Space Complexity: O(n²) for recursion stack
1 parent fa8ea8e commit 9cc1af8

2 files changed

Lines changed: 185 additions & 124 deletions

File tree

src/main/java/com/thealgorithms/backtracking/SudokuSolver.java

Lines changed: 106 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2,150 +2,135 @@
22

33
/**
44
* Sudoku Solver using Backtracking Algorithm
5-
* Solves a 9x9 Sudoku puzzle by filling empty cells with valid digits (1-9)
6-
*
7-
* @author Navadeep0007
5+
*
6+
* This class solves a partially filled 9×9 Sudoku board by finding a valid arrangement
7+
* where every row, column, and 3×3 subgrid contains digits 1-9 exactly once.
8+
*
9+
* Time Complexity: O(9^(n*n)) in worst case, where n=9
10+
* Space Complexity: O(n*n) for recursion stack
11+
*
12+
* @author TheAlgorithms
813
*/
9-
public final class SudokuSolver {
14+
public class SudokuSolver {
1015

11-
private static final int GRID_SIZE = 9;
16+
private static final int SIZE = 9;
1217
private static final int SUBGRID_SIZE = 3;
13-
private static final int EMPTY_CELL = 0;
14-
15-
private SudokuSolver() {
16-
// Utility class, prevent instantiation
17-
}
18+
private static final int EMPTY = 0;
1819

1920
/**
20-
* Solves the Sudoku puzzle using backtracking
21-
*
22-
* @param board 9x9 Sudoku board with 0 representing empty cells
23-
* @return true if puzzle is solved, false otherwise
21+
* Solves the given Sudoku board using backtracking.
22+
* Modifies the board in-place.
23+
*
24+
* @param board 9×9 2D array representing the Sudoku board (0 = empty cell)
25+
* @return true if a valid solution exists, false otherwise
2426
*/
2527
public static boolean solveSudoku(int[][] board) {
26-
if (board == null || board.length != GRID_SIZE) {
27-
return false;
28-
}
29-
30-
for (int row = 0; row < GRID_SIZE; row++) {
31-
if (board[row].length != GRID_SIZE) {
32-
return false;
33-
}
34-
}
35-
36-
return solve(board);
37-
}
28+
for (int row = 0; row < SIZE; row++) {
29+
for (int col = 0; col < SIZE; col++) {
30+
// Find an empty cell
31+
if (board[row][col] == EMPTY) {
32+
// Try digits 1-9
33+
for (int num = 1; num <= SIZE; num++) {
34+
if (isValid(board, row, col, num)) {
35+
// Place the number
36+
board[row][col] = num;
3837

39-
/**
40-
* Recursive helper method to solve the Sudoku puzzle
41-
*
42-
* @param board the Sudoku board
43-
* @return true if solution is found, false otherwise
44-
*/
45-
private static boolean solve(int[][] board) {
46-
for (int row = 0; row < GRID_SIZE; row++) {
47-
for (int col = 0; col < GRID_SIZE; col++) {
48-
if (board[row][col] == EMPTY_CELL) {
49-
for (int number = 1; number <= GRID_SIZE; number++) {
50-
if (isValidPlacement(board, row, col, number)) {
51-
board[row][col] = number;
52-
53-
if (solve(board)) {
38+
// Recursively try to solve the rest
39+
if (solveSudoku(board)) {
5440
return true;
5541
}
5642

57-
// Backtrack
58-
board[row][col] = EMPTY_CELL;
43+
// Backtrack if no solution found
44+
board[row][col] = EMPTY;
5945
}
6046
}
47+
// No valid number found, backtrack
6148
return false;
6249
}
6350
}
6451
}
52+
// All cells filled successfully
6553
return true;
6654
}
6755

6856
/**
69-
* Checks if placing a number at given position is valid
70-
*
57+
* Checks if placing a number at a given position is valid.
58+
*
7159
* @param board the Sudoku board
7260
* @param row row index
7361
* @param col column index
74-
* @param number number to place (1-9)
75-
* @return true if placement is valid, false otherwise
62+
* @param num number to place (1-9)
63+
* @return true if placement is valid
7664
*/
77-
private static boolean isValidPlacement(int[][] board, int row, int col, int number) {
78-
return !isNumberInRow(board, row, number) && !isNumberInColumn(board, col, number) && !isNumberInSubgrid(board, row, col, number);
65+
private static boolean isValid(int[][] board, int row, int col, int num) {
66+
// Check row constraint
67+
if (!isRowValid(board, row, num)) {
68+
return false;
69+
}
70+
71+
// Check column constraint
72+
if (!isColumnValid(board, col, num)) {
73+
return false;
74+
}
75+
76+
// Check 3×3 subgrid constraint
77+
if (!isSubgridValid(board, row, col, num)) {
78+
return false;
79+
}
80+
81+
return true;
7982
}
8083

8184
/**
82-
* Checks if number exists in the given row
83-
*
84-
* @param board the Sudoku board
85-
* @param row row index
86-
* @param number number to check
87-
* @return true if number exists in row, false otherwise
85+
* Checks if a number already exists in the given row.
8886
*/
89-
private static boolean isNumberInRow(int[][] board, int row, int number) {
90-
for (int col = 0; col < GRID_SIZE; col++) {
91-
if (board[row][col] == number) {
92-
return true;
87+
private static boolean isRowValid(int[][] board, int row, int num) {
88+
for (int col = 0; col < SIZE; col++) {
89+
if (board[row][col] == num) {
90+
return false;
9391
}
9492
}
95-
return false;
93+
return true;
9694
}
9795

9896
/**
99-
* Checks if number exists in the given column
100-
*
101-
* @param board the Sudoku board
102-
* @param col column index
103-
* @param number number to check
104-
* @return true if number exists in column, false otherwise
97+
* Checks if a number already exists in the given column.
10598
*/
106-
private static boolean isNumberInColumn(int[][] board, int col, int number) {
107-
for (int row = 0; row < GRID_SIZE; row++) {
108-
if (board[row][col] == number) {
109-
return true;
99+
private static boolean isColumnValid(int[][] board, int col, int num) {
100+
for (int row = 0; row < SIZE; row++) {
101+
if (board[row][col] == num) {
102+
return false;
110103
}
111104
}
112-
return false;
105+
return true;
113106
}
114107

115108
/**
116-
* Checks if number exists in the 3x3 subgrid
117-
*
118-
* @param board the Sudoku board
119-
* @param row row index
120-
* @param col column index
121-
* @param number number to check
122-
* @return true if number exists in subgrid, false otherwise
109+
* Checks if a number already exists in the 3×3 subgrid.
123110
*/
124-
private static boolean isNumberInSubgrid(int[][] board, int row, int col, int number) {
125-
int subgridRowStart = row - row % SUBGRID_SIZE;
126-
int subgridColStart = col - col % SUBGRID_SIZE;
127-
128-
for (int i = subgridRowStart; i < subgridRowStart + SUBGRID_SIZE; i++) {
129-
for (int j = subgridColStart; j < subgridColStart + SUBGRID_SIZE; j++) {
130-
if (board[i][j] == number) {
131-
return true;
111+
private static boolean isSubgridValid(int[][] board, int row, int col, int num) {
112+
int subgridRow = row - row % SUBGRID_SIZE;
113+
int subgridCol = col - col % SUBGRID_SIZE;
114+
115+
for (int i = subgridRow; i < subgridRow + SUBGRID_SIZE; i++) {
116+
for (int j = subgridCol; j < subgridCol + SUBGRID_SIZE; j++) {
117+
if (board[i][j] == num) {
118+
return false;
132119
}
133120
}
134121
}
135-
return false;
122+
return true;
136123
}
137124

138125
/**
139-
* Prints the Sudoku board
140-
*
141-
* @param board the Sudoku board
126+
* Prints the Sudoku board in a readable format.
142127
*/
143128
public static void printBoard(int[][] board) {
144-
for (int row = 0; row < GRID_SIZE; row++) {
129+
for (int row = 0; row < SIZE; row++) {
145130
if (row % SUBGRID_SIZE == 0 && row != 0) {
146131
System.out.println("-----------");
147132
}
148-
for (int col = 0; col < GRID_SIZE; col++) {
133+
for (int col = 0; col < SIZE; col++) {
149134
if (col % SUBGRID_SIZE == 0 && col != 0) {
150135
System.out.print("|");
151136
}
@@ -154,4 +139,32 @@ public static void printBoard(int[][] board) {
154139
System.out.println();
155140
}
156141
}
157-
}
142+
143+
/**
144+
* Main method demonstrating Sudoku solver functionality.
145+
*/
146+
public static void main(String[] args) {
147+
// Example Sudoku puzzle (0 = empty cell)
148+
int[][] board = {
149+
{5, 3, 0, 0, 7, 0, 0, 0, 0},
150+
{6, 0, 0, 1, 9, 5, 0, 0, 0},
151+
{0, 9, 8, 0, 0, 0, 0, 6, 0},
152+
{8, 0, 0, 0, 6, 0, 0, 0, 3},
153+
{4, 0, 0, 8, 0, 3, 0, 0, 1},
154+
{7, 0, 0, 0, 2, 0, 0, 0, 6},
155+
{0, 6, 0, 0, 0, 0, 2, 8, 0},
156+
{0, 0, 0, 4, 1, 9, 0, 0, 5},
157+
{0, 0, 0, 0, 8, 0, 0, 7, 9}
158+
};
159+
160+
System.out.println("Sudoku Puzzle:");
161+
printBoard(board);
162+
163+
if (solveSudoku(board)) {
164+
System.out.println("\nSolved Sudoku:");
165+
printBoard(board);
166+
} else {
167+
System.out.println("\nNo solution exists for this Sudoku puzzle.");
168+
}
169+
}
170+
}
Lines changed: 79 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,101 @@
11
package com.thealgorithms.backtracking;
22

3-
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
43
import static org.junit.jupiter.api.Assertions.assertFalse;
54
import static org.junit.jupiter.api.Assertions.assertTrue;
6-
75
import org.junit.jupiter.api.Test;
86

9-
class SudokuSolverTest {
7+
public class SudokuSolverTest {
108

119
@Test
12-
void testSolveSudokuEasyPuzzle() {
13-
int[][] 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}};
14-
15-
assertTrue(SudokuSolver.solveSudoku(board));
16-
17-
int[][] expected = {{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}};
10+
public void testSolvableSudoku() {
11+
int[][] board = {
12+
{5, 3, 0, 0, 7, 0, 0, 0, 0},
13+
{6, 0, 0, 1, 9, 5, 0, 0, 0},
14+
{0, 9, 8, 0, 0, 0, 0, 6, 0},
15+
{8, 0, 0, 0, 6, 0, 0, 0, 3},
16+
{4, 0, 0, 8, 0, 3, 0, 0, 1},
17+
{7, 0, 0, 0, 2, 0, 0, 0, 6},
18+
{0, 6, 0, 0, 0, 0, 2, 8, 0},
19+
{0, 0, 0, 4, 1, 9, 0, 0, 5},
20+
{0, 0, 0, 0, 8, 0, 0, 7, 9}
21+
};
1822

19-
assertArrayEquals(expected, board);
23+
assertTrue(SudokuSolver.solveSudoku(board), "Sudoku should be solvable");
24+
assertBoardValid(board);
2025
}
2126

2227
@Test
23-
void testSolveSudokuHardPuzzle() {
24-
int[][] board = {{0, 0, 0, 0, 0, 0, 6, 8, 0}, {0, 0, 0, 0, 7, 3, 0, 0, 9}, {3, 0, 9, 0, 0, 0, 0, 4, 5}, {4, 9, 0, 0, 0, 0, 0, 0, 0}, {8, 0, 3, 0, 5, 0, 9, 0, 2}, {0, 0, 0, 0, 0, 0, 0, 3, 6}, {9, 6, 0, 0, 0, 0, 3, 0, 8}, {7, 0, 0, 6, 8, 0, 0, 0, 0}, {0, 2, 8, 0, 0, 0, 0, 0, 0}};
28+
public void testUnsolvableSudoku() {
29+
int[][] board = {
30+
{5, 3, 5, 0, 7, 0, 0, 0, 0}, // Row has duplicate 5s
31+
{6, 0, 0, 1, 9, 5, 0, 0, 0},
32+
{0, 9, 8, 0, 0, 0, 0, 6, 0},
33+
{8, 0, 0, 0, 6, 0, 0, 0, 3},
34+
{4, 0, 0, 8, 0, 3, 0, 0, 1},
35+
{7, 0, 0, 0, 2, 0, 0, 0, 6},
36+
{0, 6, 0, 0, 0, 0, 2, 8, 0},
37+
{0, 0, 0, 4, 1, 9, 0, 0, 5},
38+
{0, 0, 0, 0, 8, 0, 0, 7, 9}
39+
};
2540

26-
assertTrue(SudokuSolver.solveSudoku(board));
41+
assertFalse(SudokuSolver.solveSudoku(board), "Sudoku should not be solvable");
2742
}
2843

2944
@Test
30-
void testSolveSudokuAlreadySolved() {
31-
int[][] 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}};
45+
public void testCompleteBoard() {
46+
int[][] board = {
47+
{5, 3, 4, 6, 7, 8, 9, 1, 2},
48+
{6, 7, 2, 1, 9, 5, 3, 4, 8},
49+
{1, 9, 8, 3, 4, 2, 5, 6, 7},
50+
{8, 5, 9, 7, 6, 1, 4, 2, 3},
51+
{4, 2, 6, 8, 5, 3, 7, 9, 1},
52+
{7, 1, 3, 9, 2, 4, 8, 5, 6},
53+
{9, 6, 1, 5, 3, 7, 2, 8, 4},
54+
{2, 8, 7, 4, 1, 9, 6, 3, 5},
55+
{3, 4, 5, 2, 8, 6, 1, 7, 9}
56+
};
3257

33-
assertTrue(SudokuSolver.solveSudoku(board));
58+
assertTrue(SudokuSolver.solveSudoku(board), "Already solved Sudoku should return true");
59+
assertBoardValid(board);
3460
}
3561

36-
@Test
37-
void testSolveSudokuInvalidSize() {
38-
int[][] board = {{1, 2, 3}, {4, 5, 6}};
39-
assertFalse(SudokuSolver.solveSudoku(board));
40-
}
62+
/**
63+
* Helper method to validate that the board is correctly solved.
64+
*/
65+
private void assertBoardValid(int[][] board) {
66+
// Check rows
67+
for (int row = 0; row < 9; row++) {
68+
boolean[] seen = new boolean[10];
69+
for (int col = 0; col < 9; col++) {
70+
int num = board[row][col];
71+
assertTrue(num >= 1 && num <= 9, "Invalid number in board");
72+
assertFalse(seen[num], "Duplicate in row " + row);
73+
seen[num] = true;
74+
}
75+
}
4176

42-
@Test
43-
void testSolveSudokuNullBoard() {
44-
assertFalse(SudokuSolver.solveSudoku(null));
45-
}
46-
47-
@Test
48-
void testSolveSudokuEmptyBoard() {
49-
int[][] board = {{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}};
77+
// Check columns
78+
for (int col = 0; col < 9; col++) {
79+
boolean[] seen = new boolean[10];
80+
for (int row = 0; row < 9; row++) {
81+
int num = board[row][col];
82+
assertFalse(seen[num], "Duplicate in column " + col);
83+
seen[num] = true;
84+
}
85+
}
5086

51-
assertTrue(SudokuSolver.solveSudoku(board));
87+
// Check 3×3 subgrids
88+
for (int boxRow = 0; boxRow < 3; boxRow++) {
89+
for (int boxCol = 0; boxCol < 3; boxCol++) {
90+
boolean[] seen = new boolean[10];
91+
for (int i = boxRow * 3; i < boxRow * 3 + 3; i++) {
92+
for (int j = boxCol * 3; j < boxCol * 3 + 3; j++) {
93+
int num = board[i][j];
94+
assertFalse(seen[num], "Duplicate in subgrid");
95+
seen[num] = true;
96+
}
97+
}
98+
}
99+
}
52100
}
53-
}
101+
}

0 commit comments

Comments
 (0)