如何实现检查Sudoku在Java中是否有效?

时间:2016-11-12 22:49:05

标签: java algorithm validation hashset sudoku

我想实现一项检查,看看数独在Java中是否有效,并且遇到了{http://leetcode.tgic.me/valid-sudoku/index.html)。

我理解它是如何验证行和列的,但是对于3x3网格验证器:

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         }

什么是offset以及它如何帮助检查3x3网格中的每个单元格?我粗暴地强迫它并先尝试x=0, y=0offset=0offset=1,但是offset=1给了int ox = 1%3 = 1;int oy = 1/3,所以{{1} },以及单元格board[0 + 1][0+(1/3)] = board[1][1/3]代表什么等等?

3 个答案:

答案 0 :(得分:1)

当你将n除以m时,两者都是int(文字或变量),结果也是int,所以1/3 - &gt; 0 因此,当 offset == 0 =&gt; ox = 0,oy = 0 offset == 1 =&gt; ox = 1,oy = 0 offset == 2 =&gt; ox = 2,oy = 0 offset == 3 - &gt; ox = 0,oy = 1 ... 因此,你将很好地循环3行和3列

答案 1 :(得分:0)

您的HashSet方法看起来很不错,但需要稍微调整一下......

假设是:当第一个块没有重复且所有块中的相同位置也是无重复时,则数据将被解析。

在你的外部循环中,你应该只使用第一个块的值。

您应该将当前值添加到“第一个块检查集”并检查所有块在同一位置具有不同的编号,内部循环具有自己的“检查集”:

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]

最大的诀窍是避免xy坐标的单独循环。

由于Java是一种面向对象的编程语言,我建议使用 objects 来确定坐标。我们可以将它们保存在一个数组中(设置一个书签,我通常会说“收集”而不是......)并用一个简单的forech循环迭代它...

此外,我们必须提前处理我们知道的固定数量的对象(每个有9个位置,每个位置有9个...) 所以我建议像这样使用Java enums: 所以你的逻辑应该是这样的:

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;
    }
}

我希望能够证明Java枚举的有用性以及良好的标识符名称的重要性。

我认为这种方法可以通过两种方式得到改善:

  • 使用Java 8 Stream API可以替换forech循环
  • 位置元素的名称以某种方式重复其构造函数值,这可能导致令人讨厌的错误。后者可以通过name()上可用的常量名称进行一些巧妙的计算来代替,但是对于这个演示,它可能已经足够好了......

答案 2 :(得分:0)

我认为最理想的答案是:

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;
}