1

I'd would like to implement a check to see if a sudoku is valid in Java, and came across (http://leetcode.tgic.me/valid-sudoku/index.html).

I understand how it is validating rows and columns, but for the 3x3 grids validator:

34         for(x = 0; x < mx; x += 3){
35             for(y = 0; y < my; y += 3){
36                 
37                 HashSet<Character> block = new HashSet<Character>();
38                 
39                 for(int offset = 0; offset < 9; offset++){
40                     int ox = offset % 3;
41                     int oy = offset / 3;
42                     
43                     char c = board[x + ox][y + oy];
44                     if(c != '.'){
45                         if(block.contains(c)) return false;
46                     
47                         block.add(c);
48                     } 
49                 }
50             }
51         }

What is offset and how does it help to check every cell in the 3x3 grid? I brute forced it and tried x=0, y=0, offset=0 and offset=1 first, but offset=1 gives int ox = 1%3 = 1; and int oy = 1/3, so the board[0 + 1][0+(1/3)] = board[1][1/3], and what does cell [1/3] represent and so on?

3 Answers 3

1

when you divide n by m, both are int's (either literals or variables) the result is also an int, so 1/3 -> 0 Hence when offset == 0 => ox=0, oy=0 offset == 1 => ox=1, oy=0 offset == 2 => ox=2, oy=0 offset == 3 -> ox=0, oy=1 ... hence you will loop nicely of the 3 rows and 3 columns

Sign up to request clarification or add additional context in comments.

7 Comments

Got it! Before I accept the answer and upvote, a few more questions. How is it that int ox = offset % 3; and int oy = offset / 3; are helping to loop through 3x3 grid? And why HashSet over others?
also, then in the case of say 0.6 would it round up to 1? or still be 0?
@KuNole Add System.out.println to see what x + ox and y + oy are at the point where it's getting the value from board. Then you should be able to see.
@ajb See it now! But why HashSet over others?
% is the modulo operator and will continiously rotate between the values 0, 1 & 2. / is the division operator, is does not round for integers, it returns the whole part in the int. So you can imagine that the % operator makes you use column 0, 1, 2, 0, 1, 2.... and the / operator makes you go through row 0 3 times (the 3 columns) and then row 1, ...
|
0

Your HashSet approach looks quite good, but needs a little tweaking...

The assumption is: when the first block is duplication free and the same position in all blocks is duplication free too, then the sudoku is solved.

In you outer loop you should go trough the values of the first block only.

You should add the current value to the "first block check set" and check, that the all the blocks have a different number at the same position by an inner loop with its own "check set":

First iteration
1## 2## 3##
### ### ###
### ### ###

4## 5## 5##
### ### ###
### ### ###

7## 8## 9##
### ### ###
### ### ###
firstBlock: [1]

second iteration
#2# #3# #4#
### ### ###
### ### ###

#5# #6# #7# 
### ### ###
### ### ###

#8# #9# #1# 
### ### ###
### ### ###
firstBlock: [1,2]

The big trick is to avoid separate loops for x and y coordinates.

Since Java is an object oriented programming language I suggest to use objects to determine the coordinates. We could hold them in an Array (set a bookmark,I usually say "Collection" instead...) and iterate ofer it with a simple forech loop...

Also we have to deal with a fix number of objects we know in advance (there a 9 blocks with 9 positions in each...) so I suggest using Java enums like this: so you logic should look like this:

public class SudokuCheck {
    enum SudokuPosition {
        p11(0, 0), p12(0, 1), p13(0, 2), 
        p21(1, 0), p22(1, 1), p23(1, 2), 
        p31(2, 0), p32(2, 1), p33(2, 2);

        private final int x;
        private final int y;
        SudokuPosition(int x, int y) {
            this.x = x;
            this.y = y;
        }
        public int getX() {return x;}    
        public int getY() {return y;}
    }

    boolean check(int[][] sudoku) {
        Set<Integer> firstBlockUniqueNumbers = new HashSet<>();
        for (SudokuPosition inBlock : SudokuPosition.values()) {
            firstBlockUniqueNumbers.add(sudoku[inBlock.x][inBlock.y]);

            Set<Integer> samePosInOtherBlocksUniqueNumbers = new HashSet<>();
            for (SudokuPosition ofBlock : SudokuPosition.values()) {
                int sameXinAll = inBlock.x + offset(ofBlock.x);
                int sameYinAll = inBlock.y + offset(ofBlock.y);
                samePosInOtherBlocksUniqueNumbers.add(sudoku[sameXinAll][sameYinAll]);
            }
            if (9 > samePosInOtherBlocksUniqueNumbers.size())
                // numbers where not unique at current block position
                // through all the blocks
                return false;
        }
        return 9 == firstBlockUniqueNumbers.size();
    }

    private int offset(int xOrY) {
        return xOrY * 3;
    }
}

I hopefully demonstrated the usefulness of Java enums and how important good identifier names are.

I think this approach could be improved in 2 ways:

  • using the Java 8 Stream API may replace the forech loops
  • the names of the position elements somehow repeat their constructor values which may lead to nasty mistakes. The latter may be replaces by some clever calculation on the constant name which is available via name(), but for this demonstration it might be good enough as it is...

Comments

0

the most optimal answer I think will be :

public boolean isValidSudoku(char[][] board) {
    if (board == null || board.length == 0) return false;
    for (int i = 0; i < board.length; i++) {
        for (int j = 0; j < board[0].length; j++) {
            if (board[i][j] == '.') continue;
            if (!isValid(board, i, j)) return false;
        }
    }
    return true;
}

public boolean isValid(char[][] board, int row, int col) {
    for (int i = 0; i < board.length; i++) {
        if (i == row) continue;
        if (board[i][col] == board[row][col]) return false;
    }
    for (int j = 0; j < board[0].length; j++) {
        if (j == col) continue;
        if (board[row][j] == board[row][col]) return false;
    }
    for (int i = (row / 3) * 3; i < (row / 3 + 1) * 3; i++) {
        for (int j = (col / 3) * 3; j < (col / 3 + 1) * 3; j++) {
            if (i == row && j == col) continue;
            if (board[i][j] == board[row][col]) return false;
        }
    }
    return true;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.