Java实现: 解数独

题目

编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

空白格用 '.' 表示。

 

算法

使用递归算法, 首先应获取第一个要填充的的位置, 即行和列, 优先处理候选值比较少的, 提升效率, 因为如果复杂度呈指数增长. 填充完毕后应判断是否正确, 如果不正确返回为空.

import java.util.HashSet;
import java.util.Set;

/**
 * Created by GuanDS on 2018/8/28.
 */
public class Test {

    public static void main(String[] args) {
        char[][] board = {
                {8, '.', '.', '.', '.', '.', '.', '.', '.'},
                {'.', 7, '.', '.', 9, '.', 2, '.', '.'},
                {'.', '.', 3, 6, '.', '.', '.', '.', '.'},
                {'.', 5, '.', '.', '.', 7, '.', '.', '.'},
                {'.', '.', '.', '.', 4, 5, 7, '.', '.'},
                {'.', '.', '.', 1, '.', '.', '.', 3, '.'},
                {'.', '.', 1, '.', '.', '.', '.', 6, 8},
                {'.', '.', 8, 5, '.', '.', '.', 1, '.'},
                {'.', 9, '.', '.', '.', '.', 4, '.', '.'}
        };

        board = fill(board);
        if (board == null) {
            System.out.println("数独无解");
            return;
        }
        System.out.printf("[");
        for (int j = 0; j < board.length; j++) {
            System.out.printf("[\"");
            for (int i = 0; i < board[0].length; i++) {
                System.out.print((int) board[j][i] + (i == board.length - 1 ? "\"]" : "\", \""));
            }
            if (j != board.length - 1) {
                System.out.printf(", ");
            }
        }
        System.out.printf("]");
    }

    // 递归调用填充
    private static char[][] fill(char[][] board) {
        int[] index = fillIndex(board);
        int i = index[0], j = index[1];
        if (i < 0 || j < 0) {
            return board;
        }

        //统计是否有重复字符
        Set<Character> setX = new HashSet<>();
        Set<Character> setY = new HashSet<>();
        for (int m = 0; m < board.length; m++) {
            if (board[j][m] != '.' && !setX.add(board[j][m])) {
                return null;
            }
        }
        for (int n = 0; n < board.length; n++) {
            if (board[n][i] != '.' && !setY.add(board[n][i])) {
                return null;
            }
        }

        // 合并行和列的每个不同字符, 找到未使用的字符
        setX.addAll(setY);
        if (setX.size() >= board.length) {
            return null;
        }
        for (int k = 1; k < board.length + 1; k++) {
            // 每个不存在的字符去做尝试
            if (!setX.contains((char) k)) {
                char[][] temp = copy(board);
                temp[j][i] = (char) k;
                char[][] result = fill(temp);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

    // 查找数组中候选数最少的一个, 优先选择, 提升效率
    private static int[] fillIndex(char[][] array) {
        int max = 0, maxI = -1, maxJ = -1;
        for (int j = 0; j < array.length; j++) {
            for (int i = 0; i < array.length; i++) {
                if (array[j][i] != '.') {
                    continue;
                }
                Set<Character> set = new HashSet<>();
                for (int m = 0; m < array.length; m++) {
                    if (array[m][i] != '.') {
                        set.add(array[m][i]);
                    }
                }
                for (int n = 0; n < array.length; n++) {
                    if (array[j][n] != '.') {
                        set.add(array[j][n]);
                    }
                }
                if (set.size() > max) {
                    max = set.size();
                    maxI = i;
                    maxJ = j;
                }

                if (max == array.length - 1) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[]{maxI, maxJ};
    }

    // 复制数组
    private static char[][] copy(char[][] array) {
        char[][] result = new char[array.length][array[0].length];
        for (int m = 0; m < array.length; m++) {
            for (int n = 0; n < array[0].length; n++) {
                result[m][n] = array[m][n];
            }
        }
        return result;
    }

}

结果

[["8", "3", "7", "2", "1", "6", "9", "4", "5"], ["1", "7", "4", "3", "9", "8", "2", "5", "6"], ["9", "8", "3", "6", "2", "1", "5", "7", "4"], ["3", "5", "2", "4", "6", "7", "1", "8", "9"], ["2", "1", "6", "8", "4", "5", "7", "9", "3"], ["4", "6", "9", "1", "5", "2", "8", "3", "7"], ["5", "2", "1", "9", "7", "4", "3", "6", "8"], ["7", "4", "8", "5", "3", "9", "6", "1", "2"], ["6", "9", "5", "7", "8", "3", "4", "2", "1"]]

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值