验证数独棋盘

时光怂恿深爱的人放手 提交于 2019-12-02 08:58:58

原题

  Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules.
  The Sudoku board could be partially filled, where empty cells are filled with the character '.'.
  这里写图片描述
  A partially filled sudoku which is valid.
  Note:
  A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated.

题目大意

  验证一个数独棋盘是否合法,数独棋盘的验证规则见链接对应的页面。
  数独棋盘是部分填满的,空的位置使用点来代替。
  注意:合法的棋盘不一定要求的可解的,只要填充的数字满足要求就可以。

解题思路

  先对行进行检查,再对列进行检查,最后检查3*3的方格。

代码实现

算法实现类

public class Solution {
    public boolean isValidSudoku(char[][] board) {
        // .的ASCII值是46,0的ASCII值是48,/的ASCII值是47
        int number = board[0].length;
        int[] record = new int[10 + 2]; //保存.到9的值,保存数据的位置在[2, 10]
        boolean isValid;
        reset(record);

        // 对行进行检查
        for (int i = 0; i < number; i++) {
            for (int j = 0; j < number; j++) {
                record[board[i][j] - '.']++;
            }

            if (!check(record)) { // 如是检查失败
                return false;
            } else { // 检查成功重置棋盘
                reset(record);
            }
        }

        // 对列进行检查
        for (int i = 0; i < number; i++) {
            for (int j = 0; j < number; j++) {
                record[board[j][i] - '.']++;
            }

            if (!check(record)) { // 如是检查失败
                return false;
            } else { // 检查成功重置棋盘
                reset(record);
            }
        }

        // 检查3*3方块
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {

                for (int k = i * 3; k < (i + 1) * 3; k++) {
                    for (int l = j * 3; l < (j + 1) * 3; l++) {
                        record[board[k][l]- '.']++;
                    }
                }

                if (!check(record)) { // 如是检查失败
                    return false;
                } else { // 检查成功重置棋盘
                    reset(record);
                }
            }
        }
        return true;
    }

    private void reset(int[] a) {
        for (int i = 0; i < a.length; i++) {
            a[i] = 0;
        }


    }

    /**
     * 检查棋盘一行,一列,或者3*3的方格是否合法,如果1-9中的数字个数大于1就不合法
     *
     * @param a 验证数字
     * @return 返回结果
     */
    private boolean check(int[] a) {
        for (int i = 2; i < a.length; i++) {
            if (a[i] > 1) {
                return false;
            }
        }
        return true;
    }
}

数独问题描述

 

sudoku_1

标准的数独游戏是在一个 9 X 9 的棋盘上填写 1 – 9 这 9 个数字,规则是这样的:

  • 棋盘分成上图所示的 9 个区域(不同颜色做背景标出,每个区域是 3 X 3 的子棋盘),在每个子棋盘中填充 1 – 9 且不允许重复 ,下面简称块重复
  • 每一行不许有重复值 ,下面简称行重复
  • 每一列不许有重复值 ,下面简称列重复

如上红色框出的子区域中的亮黄色格子只能填 8。

 

 

解决思路一描述:

1.使用二维数组表示数独,不确定的数使用0填充。

2.从位置(0,0)开始遍历二维数组。

3.跳过已确定的数字,直到找到第一个不确定的数(即数值0)。如果找不到不确定的数字,则表示数独已填充完成。

4.根据当前数值0的位置,排除到同行、同列、同一个小宫格中已经出现的数字,得到当前位置所有允许填充的数字。

5.如果不存在允许填充的数字,则当前数独无解。

6.尝试填充每一个允许的数字,并递归填充下一个不确定的数字。

7.如果递归填充成功,返回成功。

8.如果递归填充失败,回退步骤6中填充的数字,并跳转到6继续尝试下一个允许的数字。

 

相关代码后续补充。

 

缺点:数独中每一个未确定的值都需要递归穷举,最大的递归层级等于未确定值的数目。

改进方式:每填充一个值,在递归填充之前,动态计算每个待填充值的候选数字,删除不再符合规则的候选数字。如果有位置的候选数字为空,则数独无解。如果有位置的候选数字只有唯一一个,则直接填充该数字。通过上述两种方式可以有效减少递归的层次。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!