算法题(解数独)

算法背景

编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
在这里插入图片描述
输入:board = [[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”],[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”],[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”],[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”],[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”],[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”],[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”],[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”],[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
输出:[[“5”,“3”,“4”,“6”,“7”,“8”,“9”,“1”,“2”],[“6”,“7”,“2”,“1”,“9”,“5”,“3”,“4”,“8”],[“1”,“9”,“8”,“3”,“4”,“2”,“5”,“6”,“7”],[“8”,“5”,“9”,“7”,“6”,“1”,“4”,“2”,“3”],[“4”,“2”,“6”,“8”,“5”,“3”,“7”,“9”,“1”],[“7”,“1”,“3”,“9”,“2”,“4”,“8”,“5”,“6”],[“9”,“6”,“1”,“5”,“3”,“7”,“2”,“8”,“4”],[“2”,“8”,“7”,“4”,“1”,“9”,“6”,“3”,“5”],[“3”,“4”,“5”,“2”,“8”,“6”,“1”,“7”,“9”]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
在这里插入图片描述

独立思考

第一步:本人没玩过数独,所以先去用网上的方法,手动解了一下这道数独,这里不展开说了,放个解题草稿。主要的方法还是题目上说的三条规则,然后就开始构思这么编写代码
在这里插入图片描述
第二步:马上想到暴力破解,当遇到没有填值的格子,可以通过那三条规则,先把不能填的值都集中起来,然后挨个遍历能用的值。比如坐标为(0,2)这个空格,先可以通过规则排除掉(3.5.6.7.8.9),所以挨个遍历,先填1,然后往下一个空格走,如果有值就跳过,没有值就按这个方法继续往下走。当出现通过那三条规则把所以的可能值都排除了,但是还没填完。就说明这个解不能用,这个时候我们就应该回溯上去,重新开始遍历,当每一种可能都遍历完,坐标到(8,8)的时候,就找到了唯一解。总结下来有几个关键点,一个是需要用到递归和回溯算法,一个是找个零界点返回错误案例,描述有点绕,可以往下看代码备注

代码示例

public static boolean success = false;

    public static void main(String[] args) {
        String[][] arrs = {{"5","3",".",".","7",".",".",".","."},{"6",".",".","1","9","5",".",".","."},{".","9","8",".",".",".",".","6","."},{"8",".",".",".","6",".",".",".","3"},{"4",".",".","8",".","3",".",".","1"},{"7",".",".",".","2",".",".",".","6"},{".","6",".",".",".",".","2","8","."},{".",".",".","4","1","9",".",".","5"},{".",".",".",".","8",".",".","7","9"}};
        String[][] oldArrs = {{"5","3",".",".","7",".",".",".","."},{"6",".",".","1","9","5",".",".","."},{".","9","8",".",".",".",".","6","."},{"8",".",".",".","6",".",".",".","3"},{"4",".",".","8",".","3",".",".","1"},{"7",".",".",".","2",".",".",".","6"},{".","6",".",".",".",".","2","8","."},{".",".",".","4","1","9",".",".","5"},{".",".",".",".","8",".",".","7","9"}};
        sudoku(arrs, oldArrs,0,0);
        System.out.println(Arrays.deepToString(arrs));
    }
public static void sudoku(String[][] arrs, String[][] oldArrs, int i, int j){
        // 成功的条件
        if (i == 8 && j == 8){
            success = true;
            return;
        }
        // 当空格本身有值就直接往下走
        if (!arrs[i][j].equals(".")){
            if (j == 8){
                sudoku(arrs, oldArrs, ++i, 0);
            } else{
                sudoku(arrs, oldArrs, i, ++j);
            }
            return;
        }
        // 按那三条规则找出排除的值
        Set<String> excludeValues = excludeValue(arrs, i, j);
        // 临界点
        if (excludeValues.size() == 10){
            return;
        }
        // 开始挨个遍历每一种可能性
        for (int k = 1;k < 10;k++){
            if (!excludeValues.contains(Integer.toString(k))){
                arrs[i][j] = Integer.toString(k);
                if (j == 8){
                    sudoku(arrs, oldArrs, i + 1, 0);
                } else{
                    sudoku(arrs, oldArrs, i, j + 1);
                }
                if (success){
                    return;
                }
                // 回溯,还原回去,这步很关键
                if (oldArrs[i][j].equals(".")){
                    arrs[i][j] = ".";
                }
            }
        }
    }

    public static Set<String> excludeValue(String[][] arrs, int i, int j){
        Set<String> excludeSet = new HashSet<>();
        for (int i1 = 0; i1 < arrs.length;i1++) {
            for (int j1 = 0; j1 < arrs[0].length; j1++) {
                // 行
                if (i1 == i) {
                    excludeSet.add(arrs[i1][j1]);
                }
                // 列
                if (j1 == j) {
                    excludeSet.add(arrs[i1][j1]);
                }
                // 九宫格
                if (i < 3){
                    v(3, j, i1, j1, arrs, excludeSet);
                } else if (i < 6){
                    v(6, j, i1, j1, arrs, excludeSet);
                } else if (i < 9){
                    v(9, j, i1, j1, arrs, excludeSet);
                }
            }
        }
        return excludeSet;
    }

    public static void v(int i, int j, int i1, int j1, String[][] arrs, Set<String> excludeSet){
        if (j < 3){
            if (i1 > i - 3 && i1 < i && j1 < 3){
                excludeSet.add(arrs[i1][j1]);
            }
        } else if (j < 6){
            if (i1 > i - 3 && i1 < i && j1 > 3 && j1 < 6){
                excludeSet.add(arrs[i1][j1]);
            }
        } else if (j < 9){
            if (i1 > i - 3 && i1 < i&& j1 > 6 && j1 < 9){
                excludeSet.add(arrs[i1][j1]);
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值