(免责声明:这个问题可能有20个不同的版本,但是阅读其中大部分还没有解决我的问题)
大家好,(相对)初学者程序员。所以我一直在尝试构建一个Sudoku回溯器,它将填补一个不完整的拼图。即使1-3行完全为空(即用0填充),它似乎工作得很好,但是当更多的盒子开始清空时(特别是在第四行的7-8列,我停止写入数字)我得到堆栈溢出错误。这是代码:
import java.util.ArrayList;
import java.util.HashSet;
public class Sudoku
{
public static int[][] puzzle = new int[9][9];
public static int filledIn = 0;
public static ArrayList<Integer> blankBoxes = new ArrayList<Integer>();
public static int currentIndex = 0;
public static int runs = 0;
/**
* Main method.
*/
public static void main(String args[])
{
//Manual input of the numbers
int[] completedNumbers = {0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,3,4,
8,9,1,2,3,4,5,6,7,
3,4,5,6,7,8,9,1,2,
6,7,8,9,1,2,3,4,5,
9,1,2,3,4,5,6,7,8};
//Adds the numbers manually to the puzzle array
ArrayList<Integer> completeArray = new ArrayList<>();
for(Integer number : completedNumbers) {
completeArray.add(number);
}
int counter = 0;
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
puzzle[i][j] = completeArray.get(counter);
counter++;
}
}
//Adds all the blank boxes to an ArrayList.
//The index is stored as 10*i + j, which can be retrieved
// via modulo and integer division.
boolean containsEmpty = false;
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
if(puzzle[i][j] == 0) {
blankBoxes.add(10*i + j);
containsEmpty = true;
}
}
}
filler(blankBoxes.get(currentIndex));
}
/**
* A general method for testing whether an array contains a
* duplicate, via a (relatively inefficient) sort.
* @param testArray The int[] that is being tested for duplicates
* @return True if there are NO duplicate, false if there
* are ANY duplicates.
*/
public static boolean checkDupl(int[] testArray) {
for(int i = 0; i < 8; i++) {
int num = testArray[i];
for(int j = i + 1; j < 9; j++) {
if(num == testArray[j] && num != 0) {
return false;
}
}
}
return true;
}
/**
* If the puzzle is not full, the filler will be run. The filler is my attempt at a backtracker.
* It stores every (i,j) for which puzzle[i][j] == 0. It then adds 1 to it's value. If the value
* is already somewhere else, it adds another 1. If it is 9, and that's already there, it loops to
* 0, and the index beforehand is rechecked.
*/
public static void filler(int indexOfBlank) {
//If the current index is equal to the size of blankBoxes, meaning that we
//went through every index of blankBoxes, meaning the puzzle is full and correct.
runs++;
if(currentIndex == blankBoxes.size()) {
System.out.println("The puzzle is full!" + "\n");
for(int i = 0; i < 9; i++) {
System.out.println();
for(int j = 0; j < 9; j++) {
System.out.print(puzzle[i][j]);
}
}
System.out.println("\n" + "The filler method was run " + runs + " times");
return;
}
//Assuming the puzzle isn't full, find the row/column of the blankBoxes index.
int row = blankBoxes.get(currentIndex) / 10;
int column = blankBoxes.get(currentIndex) % 10;
//Adds one to the value of that box.
puzzle[row][column] = (puzzle[row][column] + 1);
//Just used as a breakpoint for a debugger.
if(row == 4 && column == 4){
int x = 0;
}
//If the value is 10, meaning it went through all the possible values:
if(puzzle[row][column] == 10) {
//Do filler() on the previous box
puzzle[row][column] = 0;
currentIndex--;
filler(currentIndex);
}
//If the number is 1-9, but there are duplicates:
else if(!(checkSingleRow(row) && checkSingleColumn(column) && checkSingleBox(row, column))) {
//Do filler() on the same box.
filler(currentIndex);
}
//If the number is 1-9, and there is no duplicate:
else {
currentIndex++;
filler(currentIndex);
}
}
/**
* Used to check if a single row has any duplicates or not. This is called by the
* filler method.
* @param row
* @return
*/
public static boolean checkSingleRow(int row) {
return checkDupl(puzzle[row]);
}
/**
* Used to check if a single column has any duplicates or not.
* filler method, as well as the checkColumns of the checker.
* @param column
* @return
*/
public static boolean checkSingleColumn(int column) {
int[] singleColumn = new int[9];
for(int i = 0; i < 9; i++) {
singleColumn[i] = puzzle[i][column];
}
return checkDupl(singleColumn);
}
public static boolean checkSingleBox(int row, int column) {
//Makes row and column be the first row and the first column of the box in which
//this specific cell appears. So, for example, the box at puzzle[3][7] will iterate
//through a box from rows 3-6 and columns 6-9 (exclusive).
row = (row / 3) * 3;
column = (column / 3) * 3;
//Iterates through the box
int[] newBox = new int[9];
int counter = 0;
for(int i = row; i < row + 3; i++) {
for(int j = row; j < row + 3; j++) {
newBox[counter] = puzzle[i][j];
counter++;
}
}
return checkDupl(newBox);
}
}
为什么我称之为奇怪的错误?原因如下:
我假设我只编写了低效的代码,因此虽然它不是一个实际的无限递归,但它足以调用Stack Overflow Error。但如果有人看到一个明显的问题,我很乐意听到它。谢谢!
答案 0 :(得分:1)
您的代码没有回溯。回溯意味着在失败时返回:
if(puzzle[row][column] == 10) {
puzzle[row][column] = 0;
currentIndex--;
filler(currentIndex);// but every fail you go deeper
}
必须有类似的东西:
public boolean backtrack(int currentIndex) {
if (NoBlankBoxes())
return true;
for (int i = 1; i <= 9; ++i) {
if (NoDuplicates()) {
puzzle[row][column] = i;
++currentIndex;
if (backtrack(currentIndex) == true) {
return true;
}
puzzle[row][column] = 0;
}
}
return false;
}