Java實現: 解數獨
阿新 • • 發佈:2019-02-18
題目
編寫一個程式,通過已填充的空格來解決數獨問題。
一個數獨的解法需遵循如下規則:
- 數字
1-9
在每一行只能出現一次。 - 數字
1-9
在每一列只能出現一次。 - 數字
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"]]