Java数独求解器无法正常工作,求问题排查与修复指导
数独求解器问题分析与修复
我仔细梳理了你的代码和核心思路——通过排除行、列、3x3宫的已确定数字来缩小每个单元格的候选解,这个方向是完全正确的!但代码里有几个关键逻辑漏洞导致程序无法正常推进,下面是具体的问题分析和修复方案:
核心问题点
空单元格的标记逻辑混乱
你初始用0表示空单元格,但处理后会直接把候选解列表替换进去,导致空单元格的第一个元素不再是0,后续循环无法识别这些未确定的单元格,自然也就不会继续处理它们。排除列表的收集逻辑错误
你的toCompare方法会把所有非0的单元格值都当成已确定数字收集,但实际上只有当单元格的候选列表长度为1时,才是真正确定的数字;未确定单元格的候选解不能用来排除其他单元格的可能性。初始化逻辑不完整
对于输入的空单元格(0),你只初始化了一个包含0的列表,而不是初始化为所有可能的数字(1-9),这直接导致后续无法正确生成候选解。打印逻辑无法区分状态
打印时只取每个单元格列表的第一个元素,既无法区分已确定和未确定的单元格,也看不到候选解的变化情况,不利于调试和观察进度。
修复后的完整代码
import java.util.ArrayList; import java.util.Scanner; public class SudokuBoard { public static void main(String[] args) { Scanner scan = new Scanner(System.in); // 三维ArrayList结构:行 -> 单元格 -> 候选解列表 ArrayList<ArrayList<ArrayList<Integer>>> board = new ArrayList<>(); // 初始化数独板:输入数字,非0直接设为确定值(列表长度1),0初始化为1-9的候选列表 for (int i = 0; i < 9; i++) { ArrayList<ArrayList<Integer>> row = new ArrayList<>(); for (int j = 0; j < 9; j++) { ArrayList<Integer> candidates = new ArrayList<>(); int input = scan.nextInt(); if (input != 0) { candidates.add(input); } else { // 空单元格初始化为所有可能数字 for (int num = 1; num <= 9; num++) { candidates.add(num); } } row.add(candidates); } board.add(row); } System.out.println("初始数独:"); printBoard(board); boolean hasProgress; int iterationCount = 0; // 循环迭代,直到没有单元格的候选解能被缩小 do { hasProgress = false; iterationCount++; // 遍历每个单元格 for (int row = 0; row < 9; row++) { for (int col = 0; col < 9; col++) { ArrayList<Integer> cell = board.get(row).get(col); // 只处理未确定的单元格(候选列表长度>1) if (cell.size() > 1) { // 获取当前行、列、3x3宫的已确定数字 ArrayList<Integer> usedInRow = getUsedNumbers(board, "row", row); ArrayList<Integer> usedInCol = getUsedNumbers(board, "col", col); ArrayList<Integer> usedInBox = getUsedNumbersInBox(board, row, col); // 筛选出不在已使用列表中的候选数字 ArrayList<Integer> newCandidates = new ArrayList<>(); for (int num : cell) { if (!usedInRow.contains(num) && !usedInCol.contains(num) && !usedInBox.contains(num)) { newCandidates.add(num); } } // 如果候选解数量有变化,说明有进展 if (!newCandidates.equals(cell)) { board.get(row).set(col, newCandidates); hasProgress = true; } } } } System.out.println("\n第" + iterationCount + "次迭代后:"); printBoard(board); } while (hasProgress); // 检查是否完全解决 boolean isSolved = true; for (int row = 0; row < 9; row++) { for (int col = 0; col < 9; col++) { if (board.get(row).get(col).size() != 1) { isSolved = false; break; } } if (!isSolved) break; } if (isSolved) { System.out.println("\n数独已成功解决!"); } else { System.out.println("\n当前排除法无法完全解决此数独,需要引入回溯法等更复杂的策略哦。"); } scan.close(); } // 获取指定行/列的已确定数字 public static ArrayList<Integer> getUsedNumbers(ArrayList<ArrayList<ArrayList<Integer>>> board, String type, int index) { ArrayList<Integer> used = new ArrayList<>(); if (type.equals("row")) { for (int col = 0; col < 9; col++) { ArrayList<Integer> cell = board.get(index).get(col); if (cell.size() == 1) { used.add(cell.get(0)); } } } else if (type.equals("col")) { for (int row = 0; row < 9; row++) { ArrayList<Integer> cell = board.get(row).get(index); if (cell.size() == 1) { used.add(cell.get(0)); } } } return used; } // 获取指定单元格所在3x3宫的已确定数字 public static ArrayList<Integer> getUsedNumbersInBox(ArrayList<ArrayList<ArrayList<Integer>>> board, int row, int col) { ArrayList<Integer> used = new ArrayList<>(); int boxRowStart = (row / 3) * 3; int boxColStart = (col / 3) * 3; for (int i = boxRowStart; i < boxRowStart + 3; i++) { for (int j = boxColStart; j < boxColStart + 3; j++) { ArrayList<Integer> cell = board.get(i).get(j); if (cell.size() == 1) { used.add(cell.get(0)); } } } return used; } // 打印数独板:已确定的显示数字,未确定的显示0,添加宫格分隔线 public static void printBoard(ArrayList<ArrayList<ArrayList<Integer>>> board) { for (int row = 0; row < 9; row++) { // 每3行打印分隔线 if (row % 3 == 0 && row != 0) { System.out.println("---------------------"); } for (int col = 0; col < 9; col++) { // 每3列打印分隔符 if (col % 3 == 0 && col != 0) { System.out.print("| "); } ArrayList<Integer> cell = board.get(row).get(col); if (cell.size() == 1) { System.out.print(cell.get(0) + " "); } else { System.out.print("0 "); } } System.out.println(); } } }
关键改进说明
修正初始化逻辑
- 输入非0数字时,直接将单元格设为仅包含该数字的列表(表示已确定)。
- 输入0时,初始化为包含1-9的列表(表示所有可能候选)。
调整单元格识别逻辑
- 不再用
0标记未确定单元格,而是通过候选列表长度是否大于1来判断,彻底避免了替换候选列表后无法识别的问题。
- 不再用
修复排除列表收集
- 所有收集已使用数字的方法,只收集候选列表长度为1的单元格值,确保排除的都是真正已经填入的确定数字。
优化循环逻辑
- 使用
hasProgress标记判断是否有单元格的候选解被缩小,只有当有进展时才继续循环,避免无效迭代。
- 使用
增强打印效果
- 添加了3x3宫的分隔线,更符合数独的视觉格式;未确定单元格显示
0,已确定的显示数字,便于观察进度。
- 添加了3x3宫的分隔线,更符合数独的视觉格式;未确定单元格显示
添加解决状态检查
- 循环结束后检查是否所有单元格都已确定,若未完全解决,提示需要更复杂的策略(比如回溯法,因为仅靠排除法无法解决所有高难度数独)。
内容的提问来源于stack exchange,提问作者Rohan Harish
相关产品推荐
相关产品推荐

