原题链接:https://leetcode-cn.com/problems/sudoku-solver/description/
题目描述:
知识点:递归,回溯
基本思路:
(1)新建一个成员变量retBoard,该成员变量是一个char类型的二维数组,数组维数是9 × 9的。新建一个内部类Point,该类有两个成员变量i和j,分别记录点的横、纵坐标,并有一个带参数的构造方法可以给i和j变量赋初值。
(2)遍历整个九宫格,寻找到其中内容为'.'的点,并将其坐标作为参数新建一个Point类型的对象将其加入到一个ArrayList<Point>类型的数组arrayList中,该数组用以记录需要填充的格子的点。
(3)调用函数fillNumber(char[][] board, ArrayList<Point> arrayList, int index)来给arrayList的第index个点赋值。在该函数中,递归地为整张九宫格中的'.'填充新的数字。
a.如果index == arrayList.size(),我们用retBoard数组记录下board数组中各个元素的值,并返回。
b.遍历数字1 - 9,如果arrayList中第index个点的坐标所对应的位置可以填充,就修改board对应位置的值,并尝试着递归地调用fillNumber函数填充下一个点。注意在递归调用之后,如果该方向是错误的,需要注意函数回溯过程时变量的回溯,需要将board对应位置的值重新置为'.'。
JAVA代码:
public class Solution {
private char[][] retBoard = new char[9][9];
private class Point {
int i;
int j;
public Point(int i, int j) {
this.i = i;
this.j = j;
}
}
public void solveSudoku(char[][] board) {
//在arrayList中保存需要填充的空格的横坐标和纵坐标
ArrayList<Point> arrayList = new ArrayList<>();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if(board[i][j] == '.')
arrayList.add(new Point(i, j));
}
}
fillNumber(board, arrayList, 0);
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
board[i][j] = retBoard[i][j];
}
}
}
//将要给arrayList中的第index个点进行填充值
private void fillNumber(char[][] board, ArrayList<Point> arrayList, int index) {
if(index == arrayList.size()) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
retBoard[i][j] = board[i][j];
}
}
return;
}
int indexi = arrayList.get(index).i;
int indexj = arrayList.get(index).j;
for (int num = 1; num <= 9; num++) {
if(canFillThisNum(board, indexi, indexj, num)) {
board[indexi][indexj] = String.valueOf(num).charAt(0);
fillNumber(board, arrayList, index + 1);
board[indexi][indexj] = '.';
}
}
}
private boolean canFillThisNum(char[][] board, int i, int j, int num) {
if(board[i][j] != '.') {
return false;
}
HashSet<Character> hashSet = new HashSet<>();
for (int k = 0; k < 9; k++) {
if(board[i][k] != '.') {
hashSet.add(board[i][k]);
}
if(board[k][j] != '.') {
hashSet.add(board[k][j]);
}
}
if(area(i, j) == 1) {
for (int k = 0; k <= 2; k++) {
for (int p = 0; p <= 2; p++) {
if(board[k][p] != '.') {
hashSet.add(board[k][p]);
}
}
}
}else if(area(i, j) == 2) {
for (int k = 0; k <= 2; k++) {
for (int p = 3; p <= 5; p++) {
if(board[k][p] != '.') {
hashSet.add(board[k][p]);
}
}
}
}else if(area(i, j) == 3) {
for (int k = 0; k <= 2; k++) {
for (int p = 6; p <= 8; p++) {
if(board[k][p] != '.') {
hashSet.add(board[k][p]);
}
}
}
}else if(area(i, j) == 4) {
for (int k = 3; k <= 5; k++) {
for (int p = 0; p <= 2; p++) {
if(board[k][p] != '.') {
hashSet.add(board[k][p]);
}
}
}
}else if(area(i, j) == 5) {
for (int k = 3; k <= 5; k++) {
for (int p = 3; p <= 5; p++) {
if(board[k][p] != '.') {
hashSet.add(board[k][p]);
}
}
}
}else if(area(i, j) == 6) {
for (int k = 3; k <= 5; k++) {
for (int p = 6; p <= 8; p++) {
if(board[k][p] != '.') {
hashSet.add(board[k][p]);
}
}
}
}else if(area(i, j) == 7) {
for (int k = 6; k <= 8; k++) {
for (int p = 0; p <= 2; p++) {
if(board[k][p] != '.') {
hashSet.add(board[k][p]);
}
}
}
}else if(area(i, j) == 8) {
for (int k = 6; k <= 8; k++) {
for (int p = 3; p <= 5; p++) {
if(board[k][p] != '.') {
hashSet.add(board[k][p]);
}
}
}
}else if(area(i, j) == 9) {
for (int k = 6; k <= 8; k++) {
for (int p = 6; p <= 8; p++) {
if(board[k][p] != '.') {
hashSet.add(board[k][p]);
}
}
}
}
if(hashSet.contains(String.valueOf(num).charAt(0))) {
return false;
}
return true;
}
private int area(int i, int j) {
if(i >= 0 && i <= 2 && j >= 0 && j <= 2) {
return 1;
}else if(i >= 0 && i <= 2 && j >= 3 && j <= 5) {
return 2;
}else if(i >= 0 && i <= 2 && j >= 6 && j <= 8) {
return 3;
}else if(i >= 3 && i <= 5 && j >= 0 && j <= 2) {
return 4;
}else if(i >= 3 && i <= 5 && j >= 3 && j <= 5) {
return 5;
}else if(i >= 3 && i <= 5 && j >= 6 && j <= 8) {
return 6;
}else if(i >= 6 && i <= 8 && j >= 0 && j <= 2) {
return 7;
}else if(i >= 6 && i <= 8 && j >= 3 && j <= 5) {
return 8;
}else if(i >= 6 && i <= 8 && j >= 6 && j <= 8) {
return 9;
}
return -1;
}
public static void main(String[] args) {
String[][] stringBoard = {{"5","3",".",".","7",".",".",".","."},
{"6",".",".","1","9","5",".",".","."},
{".","9","8",".",".",".",".","6","."},
{"8",".",".",".","6",".",".",".","3"},
{"4",".",".","8",".","3",".",".","1"},
{"7",".",".",".","2",".",".",".","6"},
{".","6",".",".",".",".","2","8","."},
{".",".",".","4","1","9",".",".","5"},
{".",".",".",".","8",".",".","7","9"}};
char[][] board = new char[9][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
board[i][j] = stringBoard[i][j].charAt(0);
}
}
System.out.println("做题前:");
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
new Solution().solveSudoku(board);
System.out.println("做题后:");
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}
}
复杂度分析:
时间复杂度:因为需要递归调用函数n次,每次递归调用最多有9个方向可以走,所以时间复杂度为O(9 ^ n),其中n为九宫格中'.'的数量。
空间复杂度:因为需要递归调用函数n次,所以空间复杂度为O(n)