1. 程式人生 > >Java實現: 解數獨

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"]]