Skip to content

Commit 50340c5

Browse files
feat: implement Sudoku Solver with full coverage and fixed legacy Javadoc warnings #6929
1 parent e814d97 commit 50340c5

5 files changed

Lines changed: 152 additions & 118 deletions

File tree

Lines changed: 102 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,159 @@
11
package com.thealgorithms.backtracking;
22

33
/**
4-
* Sudoku Solver using Backtracking Algorithm
5-
* Solves a 9x9 Sudoku puzzle by filling empty cells with valid digits (1-9)
4+
* The {@code SudokuSolver} class provides a solution to Sudoku puzzles
5+
* Solves the puzzle by a depth-first backtracking algorithm.
66
*
7-
* @author Navadeep0007
7+
* The algorithm fills empty cells (represented by 0) by trying digits 1-9
8+
* and recursively verifying if they lead to a valid solution.
9+
*
10+
* Time Complexity: O(9^{n*n}) where n is the dimension of the grid.
11+
* Space Complexity: O(n*n) to store the board and recursion stack.
12+
*
13+
* @author subhammohanty-sys
814
*/
9-
public final class SudokuSolver {
1015

11-
private static final int GRID_SIZE = 9;
12-
private static final int SUBGRID_SIZE = 3;
13-
private static final int EMPTY_CELL = 0;
16+
public class SudokuSolver {
1417

15-
private SudokuSolver() {
16-
// Utility class, prevent instantiation
17-
}
18+
private SudokuSolver(){}
1819

1920
/**
20-
* Solves the Sudoku puzzle using backtracking
21+
* Entry point to solve the Sudoku puzzle.
22+
* Performs a pre-validation check before starting the recursion.
2123
*
22-
* @param board 9x9 Sudoku board with 0 representing empty cells
23-
* @return true if puzzle is solved, false otherwise
24+
* @param board 2D array representing the Sudoku grid (0 for empty cells).
25+
* @return true if the board is solvable, false otherwise.
2426
*/
2527
public static boolean solveSudoku(int[][] board) {
26-
if (board == null || board.length != GRID_SIZE) {
28+
if (board == null || board.length !=9 || !isBoardValid(board)) {
2729
return false;
2830
}
2931

30-
for (int row = 0; row < GRID_SIZE; row++) {
31-
if (board[row].length != GRID_SIZE) {
32-
return false;
33-
}
34-
}
35-
3632
return solve(board);
3733
}
3834

35+
3936
/**
40-
* Recursive helper method to solve the Sudoku puzzle
37+
* Validates the initial state of the board to ensure no existing
38+
* numbers violate Sudoku rules.
4139
*
42-
* @param board the Sudoku board
43-
* @return true if solution is found, false otherwise
40+
* @param board The Sudoku grid.
41+
* @return true if the initial board configuration is valid.
4442
*/
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;
5243

53-
if (solve(board)) {
54-
return true;
55-
}
5644

57-
// Backtrack
58-
board[row][col] = EMPTY_CELL;
59-
}
45+
private static boolean isBoardValid(int[][] board) {
46+
for (int i = 0; i < 9; i++) {
47+
for (int j = 0; j < 9; j++) {
48+
if (board[i][j] != 0) {
49+
int num = board[i][j];
50+
board[i][j] = 0; // Temporarily empty to validate
51+
if (!isValid(board, i, j, num)) {
52+
return false;
6053
}
61-
return false;
54+
55+
board[i][j] = num;
6256
}
6357
}
6458
}
59+
6560
return true;
6661
}
6762

6863
/**
69-
* Checks if placing a number at given position is valid
64+
* Checks if placing a number in a specific cell is legal.
7065
*
71-
* @param board the Sudoku board
72-
* @param row row index
73-
* @param col column index
74-
* @param number number to place (1-9)
75-
* @return true if placement is valid, false otherwise
66+
* @param board The Sudoku grid.
67+
* @param row Row index.
68+
* @param col Column index.
69+
* @param num Number to place (1-9).
70+
* @return true if the placement is safe.
7671
*/
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);
79-
}
72+
private static boolean isValid(int[][] board, int row, int col, int num) {
73+
for (int i = 0; i < 9; i++) {
74+
// Check row and column constraints
75+
if (board[i][col] == num || board[row][i] == num) {
76+
return false;
77+
}
78+
}
8079

81-
/**
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
88-
*/
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;
80+
// Check 3x3 sub-grid constraints
81+
int sr = (row / 3) * 3;
82+
int sc = (col / 3) * 3;
83+
84+
for (int i = sr; i < sr + 3; i++) {
85+
for (int j = sc; j < sc + 3; j++) {
86+
if (board[i][j] == num) {
87+
return false;
88+
}
9389
}
9490
}
95-
return false;
91+
return true;
9692
}
9793

9894
/**
99-
* Checks if number exists in the given column
95+
* Internal recursive helper that implements the backtracking logic.
10096
*
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+
* @param board The Sudoku grid being solved.
98+
* @return true if a solution is found.
10599
*/
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;
100+
private static boolean solve(int[][] board) {
101+
for (int row = 0; row < 9; row++) {
102+
for (int col = 0; col < 9; col++) {
103+
if (board[row][col] == 0) {
104+
for (int num = 1; num <= 9; num++) {
105+
if (isValid(board, row, col, num)) {
106+
board[row][col] = num;
107+
if (solve(board)) {
108+
return true;
109+
}
110+
board[row][col] = 0; // Backtrack
111+
}
112+
}
113+
return false;
114+
}
110115
}
111116
}
112-
return false;
117+
return true;
113118
}
114119

115120
/**
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
121+
* Demonstration of the solver.
123122
*/
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;
132-
}
133-
}
123+
public static void main(String[] args) {
124+
125+
int[][] board = {
126+
{5, 3, 0, 0, 7, 0, 0, 0, 0},
127+
{6, 0, 0, 1, 9, 5, 0, 0, 0},
128+
{0, 9, 8, 0, 0, 0, 0, 6, 0},
129+
{8, 0, 0, 0, 6, 0, 0, 0, 3},
130+
{4, 0, 0, 8, 0, 3, 0, 0, 1},
131+
{7, 0, 0, 0, 2, 0, 0, 0, 6},
132+
{0, 6, 0, 0, 0, 0, 2, 8, 0},
133+
{0, 0, 0, 4, 1, 9, 0, 0, 5},
134+
{0, 0, 0, 0, 8, 0, 0, 7, 9}
135+
};
136+
137+
if (SudokuSolver.solveSudoku(board)) {
138+
printBoard(board);
139+
} else {
140+
System.out.println("No solution exists.");
134141
}
135-
return false;
136142
}
137143

138144
/**
139-
* Prints the Sudoku board
145+
* Helper method to print the board in a readable format.
140146
*
141-
* @param board the Sudoku board
147+
* @param board The Sudoku grid.
142148
*/
143-
public static void printBoard(int[][] board) {
144-
for (int row = 0; row < GRID_SIZE; row++) {
145-
if (row % SUBGRID_SIZE == 0 && row != 0) {
146-
System.out.println("-----------");
147-
}
148-
for (int col = 0; col < GRID_SIZE; col++) {
149-
if (col % SUBGRID_SIZE == 0 && col != 0) {
150-
System.out.print("|");
151-
}
152-
System.out.print(board[row][col]);
149+
private static void printBoard(int[][] board) {
150+
for (int i = 0; i < 9; i++) {
151+
if (i % 3 == 0 && i != 0) System.out.println("---------------------");
152+
for (int j = 0; j < 9; j++) {
153+
if (j % 3 == 0 && j != 0) System.out.print("| ");
154+
System.out.print(board[i][j] + " ");
153155
}
154156
System.out.println();
155157
}
156158
}
157-
}
159+
}

src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
/**
2-
* [Brief description of what the algorithm does]
3-
* <p>
4-
* Time Complexity: O(n) [or appropriate complexity]
5-
* Space Complexity: O(n)
6-
* @author Reshma Kakkirala
7-
*/
81
package com.thealgorithms.conversions;
92

103
import java.util.Arrays;
@@ -13,6 +6,12 @@
136
import java.util.Scanner;
147

158
/**
9+
* [Brief description of what the algorithm does]
10+
* <p>
11+
* Time Complexity: O(n) [or appropriate complexity]
12+
* Space Complexity: O(n)
13+
* @author Reshma Kakkirala
14+
*
1615
* Class for converting from "any" base to "any" other base, when "any" means
1716
* from 2-36. Works by going from base 1 to decimal to base 2. Includes
1817
* auxiliary method for determining whether a number is valid for a given base.

src/main/java/com/thealgorithms/searches/InterpolationSearch.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package com.thealgorithms.searches;
2+
13
/**
24
* Interpolation Search estimates the position of the target value
35
* based on the distribution of values.
@@ -8,10 +10,7 @@
810
*
911
* Time Complexity: O(log log n) (average case)
1012
* Space Complexity: O(1)
11-
*/
12-
package com.thealgorithms.searches;
13-
14-
/**
13+
*
1514
* InterpolationSearch is an algorithm that searches for a target value within a sorted array
1615
* by estimating the position based on the values at the corners of the current search range.
1716
*

src/main/java/com/thealgorithms/searches/LinearSearch.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
package com.thealgorithms.searches;
2+
3+
import com.thealgorithms.devutils.searches.SearchAlgorithm;
4+
5+
16
/**
27
* Performs Linear Search on an array.
38
*
@@ -10,12 +15,7 @@
1015
*
1116
* Time Complexity: O(n)
1217
* Space Complexity: O(1)
13-
*/
14-
package com.thealgorithms.searches;
15-
16-
import com.thealgorithms.devutils.searches.SearchAlgorithm;
17-
18-
/**
18+
*
1919
* Linear Search is a simple searching algorithm that checks
2020
* each element of the array sequentially until the target
2121
* value is found or the array ends.

src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
44
import static org.junit.jupiter.api.Assertions.assertFalse;
55
import static org.junit.jupiter.api.Assertions.assertTrue;
6-
76
import org.junit.jupiter.api.Test;
87

98
class SudokuSolverTest {
@@ -50,4 +49,39 @@ void testSolveSudokuEmptyBoard() {
5049

5150
assertTrue(SudokuSolver.solveSudoku(board));
5251
}
52+
53+
@Test
54+
void testSolveInitiallyInvalidBoard(){
55+
int[][] board ={
56+
{5,5,0,0,0,0,0,0,0},
57+
{0,0,0,0,0,0,0,0,0},
58+
{0,0,0,0,0,0,0,0,0},
59+
{0,0,0,0,0,0,0,0,0},
60+
{0,0,0,0,0,0,0,0,0},
61+
{0,0,0,0,0,0,0,0,0},
62+
{0,0,0,0,0,0,0,0,0},
63+
{0,0,0,0,0,0,0,0,0},
64+
{0,0,0,0,0,0,0,0,0},
65+
66+
};
67+
68+
assertFalse(SudokuSolver.solveSudoku(board));
69+
}
70+
71+
@Test
72+
void testSolveSudokuUnsolvablePuzzle(){
73+
int[][] board = {
74+
{5, 1, 6, 8, 4, 9, 7, 3, 2},
75+
{3, 0, 7, 6, 0, 5, 0, 0, 0},
76+
{8, 0, 9, 1, 0, 0, 0, 6, 0},
77+
{1, 3, 0, 4, 0, 7, 0, 0, 0},
78+
{0, 0, 2, 0, 1, 0, 3, 0, 0},
79+
{0, 0, 0, 9, 0, 3, 0, 1, 7},
80+
{0, 6, 0, 0, 0, 1, 9, 0, 3},
81+
{0, 0, 0, 3, 0, 8, 5, 0, 1},
82+
{0, 0, 0, 5, 0, 0, 4, 7, 0}
83+
};
84+
board[1][1] = 5;
85+
assertFalse(SudokuSolver.solveSudoku(board));
86+
}
5387
}

0 commit comments

Comments
 (0)