【问题标题】:How to implement check if Sudoku is valid in Java?如何实现检查数独在 Java 中是否有效?
【发布时间】:2016-11-12 22:49:05
【问题描述】:

我想检查数独在 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,所以board[0 + 1][0+(1/3)] = board[1][1/3],单元格[1/3]代表什么等等?

【问题讨论】:

    标签: java algorithm validation hashset sudoku


    【解决方案1】:

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

    【讨论】:

    • 知道了!在我接受答案并投票之前,还有几个问题。 int ox = offset % 3;int oy = offset / 3; 如何帮助循环通过 3x3 网格?为什么HashSet 胜过其他人?
    • 还有,那么在0.6 的情况下,它会舍入到 1 吗?还是0
    • @KuNole 添加System.out.println 以查看x + oxy + oy 在从board 获取值的位置。那你应该可以看到了。
    • @ajb 立即查看!但是为什么 HashSet 胜过其他的呢?
    • % 是取模运算符,将在值 0、1 和 2 之间连续旋转。/ 是除法运算符,对整数不四舍五入,它返回 int 中的整个部分。因此,您可以想象 % 运算符使您使用第 0、1、2、0、1、2.... 列,而 / 运算符使您遍历第 0 行 3 次(3 列),然后是第 1 行。 ..
    【解决方案2】:

    您的 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() 获得,但对于这个演示来说,它可能已经足够好了......

    【讨论】:

      【解决方案3】:

      我认为最理想的答案是:

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

      【讨论】:

        猜你喜欢
        • 2022-08-07
        • 2011-04-21
        • 2018-08-05
        • 1970-01-01
        • 2012-01-21
        • 1970-01-01
        • 2013-12-10
        • 2023-04-11
        • 1970-01-01
        相关资源
        最近更新 更多