Skip to content

Commit b891b6b

Browse files
committed
solution for valid sudoku
1 parent 8a1fde6 commit b891b6b

File tree

3 files changed

+405
-0
lines changed

3 files changed

+405
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package validsudoku
2+
3+
// Time complexity: O(1) - The board size is fixed at 9x9, so the operations are constant time.
4+
// Space complexity: O(1) - No additional space is used that scales with input size.
5+
func IsValidSudokuBruteForce(board [][]byte) bool {
6+
check := func(nums []byte) bool {
7+
count := [9]int{}
8+
for _, ch := range nums {
9+
if ch != '.' {
10+
idx := ch - '1'
11+
if count[idx] > 0 {
12+
return false
13+
}
14+
count[idx]++
15+
}
16+
}
17+
return true
18+
}
19+
20+
// Check rows
21+
for i := 0; i < 9; i++ {
22+
if !check(board[i]) {
23+
return false
24+
}
25+
}
26+
27+
// Check columns
28+
for j := 0; j < 9; j++ {
29+
col := make([]byte, 9)
30+
for i := 0; i < 9; i++ {
31+
col[i] = board[i][j]
32+
}
33+
if !check(col) {
34+
return false
35+
}
36+
}
37+
38+
// Check 3x3 sub-boxes
39+
for boxRow := 0; boxRow < 3; boxRow++ {
40+
for boxCol := 0; boxCol < 3; boxCol++ {
41+
box := make([]byte, 0, 9)
42+
for i := 0; i < 3; i++ {
43+
for j := 0; j < 3; j++ {
44+
box = append(box, board[boxRow*3+i][boxCol*3+j])
45+
}
46+
}
47+
if !check(box) {
48+
return false
49+
}
50+
}
51+
}
52+
return true
53+
}
54+
55+
// Time complexity: O(1) - The board size is fixed at 9x9, so the operations are constant time.
56+
// Space complexity: O(1) - No additional space is used that scales with input size.
57+
func IsValidSudokuHashSet(board [][]byte) bool {
58+
rows := [9]map[byte]bool{}
59+
cols := [9]map[byte]bool{}
60+
boxes := [9]map[byte]bool{}
61+
62+
for i := range rows {
63+
rows[i] = map[byte]bool{}
64+
cols[i] = map[byte]bool{}
65+
boxes[i] = map[byte]bool{}
66+
}
67+
68+
for i := 0; i < 9; i++ {
69+
for j := 0; j < 9; j++ {
70+
val := board[i][j]
71+
if val == '.' {
72+
continue
73+
}
74+
boxIdx := (i/3)*3 + j/3
75+
76+
if rows[i][val] || cols[j][val] || boxes[boxIdx][val] {
77+
return false
78+
}
79+
rows[i][val] = true
80+
cols[j][val] = true
81+
boxes[boxIdx][val] = true
82+
}
83+
}
84+
return true
85+
}
86+
87+
// Time complexity: O(1) - The board size is fixed at 9x9, so the operations are constant time.
88+
// Space complexity: O(1) - No additional space is used that scales with input size.
89+
func IsValidSudokuBitMask(board [][]byte) bool {
90+
var rows, cols, boxes [9]int
91+
92+
for i := 0; i < 9; i++ {
93+
for j := 0; j < 9; j++ {
94+
val := board[i][j]
95+
if val == '.' {
96+
continue
97+
}
98+
bit := 1 << (val - '1')
99+
boxIdx := (i/3)*3 + j/3
100+
101+
if (rows[i]&bit) != 0 || (cols[j]&bit) != 0 || (boxes[boxIdx]&bit) != 0 {
102+
return false
103+
}
104+
105+
rows[i] |= bit
106+
cols[j] |= bit
107+
boxes[boxIdx] |= bit
108+
}
109+
}
110+
return true
111+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package validsudoku
2+
3+
import "testing"
4+
5+
func TestIsValidSudoku(t *testing.T) {
6+
cases := []struct {
7+
name string
8+
fn func([][]byte) bool
9+
board [][]byte
10+
expected bool
11+
}{
12+
// IsValidSudokuBruteForce
13+
{"Brute Force - Valid", IsValidSudokuBruteForce, validSudokuBoard(), true},
14+
{"Brute Force - Invalid Row", IsValidSudokuBruteForce, invalidRowSudokuBoard(), false},
15+
{"Brute Force - Invalid Column Only", IsValidSudokuBruteForce, invalidColOnlySudokuBoard(), false},
16+
{"Brute Force - Invalid Box Only", IsValidSudokuBruteForce, invalidBoxOnlySudokuBoard(), false},
17+
18+
// IsValidSudokuHashSet - can use original or new ones, HashSet checks "all at once"
19+
{"Hash Set - Valid", IsValidSudokuHashSet, validSudokuBoard(), true},
20+
{"Hash Set - Invalid Row", IsValidSudokuHashSet, invalidRowSudokuBoard(), false},
21+
{"Hash Set - Invalid Column", IsValidSudokuHashSet, originalInvalidColSudokuBoard(), false}, // or invalidColOnlySudokuBoard()
22+
{"Hash Set - Invalid Box", IsValidSudokuHashSet, originalInvalidBoxSudokuBoard(), false}, // or invalidBoxOnlySudokuBoard()
23+
24+
// IsValidSudokuBitMask - similar to HashSet
25+
{"BitMask - Valid", IsValidSudokuBitMask, validSudokuBoard(), true},
26+
{"BitMask - Invalid Row", IsValidSudokuBitMask, invalidRowSudokuBoard(), false},
27+
{"BitMask - Invalid Column", IsValidSudokuBitMask, originalInvalidColSudokuBoard(), false}, // or invalidColOnlySudokuBoard()
28+
{"BitMask - Invalid Box", IsValidSudokuBitMask, originalInvalidBoxSudokuBoard(), false}, // or invalidBoxOnlySudokuBoard()
29+
}
30+
31+
for _, c := range cases {
32+
t.Run(c.name, func(t *testing.T) {
33+
got := c.fn(c.board)
34+
if got != c.expected {
35+
t.Errorf("%s failed: expected %v, got %v for board:\n%v", c.name, c.expected, got, boardToString(c.board))
36+
}
37+
})
38+
}
39+
}
40+
41+
// Helper to print board for easier debugging
42+
func boardToString(board [][]byte) string {
43+
s := ""
44+
for i := 0; i < 9; i++ {
45+
for j := 0; j < 9; j++ {
46+
s += string(board[i][j]) + " "
47+
}
48+
s += "\n"
49+
}
50+
return s
51+
}
52+
53+
func validSudokuBoard() [][]byte {
54+
return [][]byte{
55+
{'5', '3', '.', '.', '7', '.', '.', '.', '.'},
56+
{'6', '.', '.', '1', '9', '5', '.', '.', '.'},
57+
{'.', '9', '8', '.', '.', '.', '.', '6', '.'},
58+
{'8', '.', '.', '.', '6', '.', '.', '.', '3'},
59+
{'4', '.', '.', '8', '.', '3', '.', '.', '1'},
60+
{'7', '.', '.', '.', '2', '.', '.', '.', '6'},
61+
{'.', '6', '.', '.', '.', '.', '2', '8', '.'},
62+
{'.', '.', '.', '4', '1', '9', '.', '.', '5'},
63+
{'.', '.', '.', '.', '8', '.', '.', '7', '9'},
64+
}
65+
}
66+
67+
func invalidRowSudokuBoard() [][]byte {
68+
board := validSudokuBoard()
69+
board[0][1] = '5' // Duplicate 5 in first row
70+
return board
71+
}
72+
73+
func invalidColOnlySudokuBoard() [][]byte {
74+
board := validSudokuBoard()
75+
board[1][0] = '8' // Col 0: 5,8,.,8,... (invalid); Row 1: 8,.,.,1,9,5,... (valid)
76+
return board
77+
}
78+
79+
func invalidBoxOnlySudokuBoard() [][]byte {
80+
board := validSudokuBoard()
81+
board[1][1] = '8' // Box 0,0: contains two '8's; Row 1 and Col 1 remain valid
82+
return board
83+
}
84+
85+
func originalInvalidColSudokuBoard() [][]byte {
86+
board := validSudokuBoard()
87+
board[1][0] = '5'
88+
return board
89+
}
90+
91+
func originalInvalidBoxSudokuBoard() [][]byte {
92+
board := validSudokuBoard()
93+
board[1][1] = '9'
94+
return board
95+
}

0 commit comments

Comments
 (0)