编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.' 表示。
示例 1:
输入:board = [["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"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
提示:
board.length == 9
board[i].length == 9
board[i][j] 是一位数字或者 '.'
题目数据 保证 输入数独仅有一个解。
python实现
class Solution(object):
def solveSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: None Do not return anything, modify board in-place instead.
"""
# rowVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
# colVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
# blockVisit, key 是每个宫格的位置,value 是一个数组标识每个数字是否出现过
# hasTry, key 是每个宫格的位置,value 是一个哈希表,标识某个数字是否尝试过
rowVisit = {}
colVisit = {}
blockVisit = {}
hasTry = {}
# visit 表示第i行第j列是否被填过
visit = [[0 for _ in range(9)] for _ in range(9)]
n = 9
# 遍历每个初始的位置
# 将rowVisit,rowVisit,blockVisit三张表填写完整,并填写hasTry和visit
for i in range(9):
for j in range(9):
if i not in rowVisit:
rowVisit[i] = [0 for _ in range(10)]
if j not in colVisit:
colVisit[j] = [0 for _ in range(10)]
blockid = (i // 3, j // 3)
if blockid not in blockVisit:
blockVisit[blockid] = [0 for _ in range(10)]
hasTry[(i//3, j//3)] = set()
for i in range(9):
for j in range(9):
if board[i][j] != '.':
rowVisit[i][ord(board[i][j])-ord('0')] = 1
colVisit[j][ord(board[i][j])-ord('0')] = 1
blockid = (i//3, j// 3)
blockVisit[blockid][ord(board[i][j])-ord('0')] = 1
visit[i][j] = 1
# 存结果,是个列表,为了防止可能有多个答案
result = []
self.process(board, 0, 0, rowVisit, colVisit, blockVisit, visit, hasTry, result)
# 将最终结果的第一个答案填回到board中
for i in range(9):
board[i] = result[0][i][:]
def process(self, board, x, y, rowVisit, colVisit, blockVisit, visit, hasTry, result):
# board 原来的题目的内存地址
# rowVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
# colVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
# blockVisit, key 是每个宫格的位置,value 是一个数组标识每个数字是否出现过
# visit 表示第i行第j列是否被填过
# hasTry, key 是每个宫格的位置,value 是一个哈希表,标识某个数字是否尝试过
# result存结果,是个列表,为了防止可能有多个答案
# 来到第10行,说明前面的格子都已经完成了
# 将结果保持在result当中,返回true
if x >= 9:
res = [row[:] for row in board]
result.append(res)
return True
# 来到第10列,说明这一行的前面的格子都已经填写完了
if y >= 9:
# 列数归1, 行数+1,尝试下一列
return self.process(board, x+1, 0, rowVisit, colVisit, blockVisit, visit, hasTry, result)
# 已经填写过这个格子了,尝试下一列
if visit[x][y] == 1:
return self.process(board, x, y+1, rowVisit, colVisit, blockVisit, visit, hasTry, result)
else:
# 定位是哪个宫格的格子
blockid = (x//3, y//3)
flag = False
# 尝试1~9 中间的每个数字
for i in range(1, 10):
# 如果有数字和当前尝试同一行、同一列、或者同一宫格。尝试下一个数字
if rowVisit[x][i] == 1 or colVisit[y][i] == 1 or blockVisit[blockid][i] == 1:
continue
# 当前行、当前列、当前宫格的数字填上
rowVisit[x][i] = 1
colVisit[y][i] = 1
blockVisit[blockid][i] = 1
# board中填好需要尝试的数字
board[x][y] = chr(ord('0') + i)
# 当前位置已经填写完毕
visit[x][y] = 1
flag |= self.process(board, x, y+1, rowVisit, colVisit, blockVisit, visit, hasTry, result)
# 回溯返回没有填过的状态
rowVisit[x][i] = 0
colVisit[y][i] = 0
blockVisit[blockid][i] = 0
board[x][y] = '.'
visit[x][y] = 0
return flag
Java实现
class Solution {
// Point 类,存放x,y的矩阵点信息
public static class Point{
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
public void solveSudoku(char[][] board) {
// rowVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
// colVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
// blockVisit, key 是每个宫格的位置,value 是一个数组标识每个数字是否出现过
// hasTry, key 是每个宫格的位置,value 是一个哈希表,标识某个数字是否尝试过
HashMap rowVisit = new HashMap<>();
HashMap colVisit = new HashMap<>();
HashMap blockVisit = new HashMap<>();
HashMap> hasTry = new HashMap<>();
// visit 表示第i行第j列是否被填过
int[][] visit = new int[9][9];
int n = 9;
// 遍历每个初始的位置
// 将rowVisit,rowVisit,blockVisit三张表填写完整,并填写hasTry和visit
for(int i=0;i());
}
}
for(int i=0; i='0'){
rowVisit.get(i)[board[i][j]-'0'] = 1;
colVisit.get(j)[board[i][j]-'0'] = 1;
Point blockid = new Point(i/3, j/ 3);
blockVisit.get(blockid)[board[i][j]-'0'] = 1;
visit[i][j] = 1;
}
}
}
// 存结果,是个列表,为了防止可能有多个答案
ArrayList result = new ArrayList<>() ;
process(board, 0, 0, rowVisit, colVisit, blockVisit, visit, hasTry,result);
// 将最终结果的第一个答案填回到board中
for(int i=0; i<9; i++){
board[i] = Arrays.copyOf(result.get(0)[i], 9);
}
}
public boolean process(char[][] board, int x, int y,
HashMap rowVisit,
HashMap colVisit,
HashMap blockVisit,
int[][] visit,
HashMap> hasTry, ArrayList result){
// board 原来的题目的内存地址
// rowVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
// colVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
// blockVisit, key 是每个宫格的位置,value 是一个数组标识每个数字是否出现过
// visit 表示第i行第j列是否被填过
// hasTry, key 是每个宫格的位置,value 是一个哈希表,标识某个数字是否尝试过
// result存结果,是个列表,为了防止可能有多个答案
// 来到第10行,说明前面的格子都已经完成了
// 将结果保持在result当中,返回true
if(x>=9){
char[][] res = new char[9][9];
for(int i=0; i<9; i++){
res[i] = Arrays.copyOf(board[i], 9);
}
result.add(res);
return true;
}
// 来到第10列,说明这一行的前面的格子都已经填写完了
if(y>=9){
// 列数归1, 行数+1,尝试下一列
return process(board, x+1, 0, rowVisit, colVisit, blockVisit, visit, hasTry, result);
}
// 已经填写过这个格子了,尝试下一列
if(visit[x][y]==1){
return process(board, x, y+1, rowVisit, colVisit, blockVisit, visit, hasTry, result);
}else{
// 定位是哪个宫格的格子
Point blockid = new Point(x/3, y/ 3);
boolean flag = false;
// 尝试1~9 中间的每个数字
for(int i=1; i<=9 ; i++){
// 如果有数字和当前尝试同一行、同一列、或者同一宫格。尝试下一个数字
if(rowVisit.get(x)[i]==1||colVisit.get(y)[i]==1||blockVisit.get(blockid)[i]==1){
continue;
}
// 当前行、当前列、当前宫格的数字填上
rowVisit.get(x)[i] = 1;
colVisit.get(y)[i] = 1;
blockVisit.get(blockid)[i] = 1;
// board中填好需要尝试的数字
board[x][y] = (char)('0' + i);
// 当前位置已经填写完毕
visit[x][y] = 1;
// 尝试下一个位置
flag |= process(board, x, y+1, rowVisit, colVisit, blockVisit, visit, hasTry, result);
// 回溯返回没有填过的状态
rowVisit.get(x)[i] = 0;
colVisit.get(y)[i] = 0;
blockVisit.get(blockid)[i] = 0;
board[x][y] = '.';
visit[x][y] = 0;
}
return flag;
}
}
}