https://leetcode-cn.com/problems/sudoku-solver/submissions/
这道题,我用的是我平时解数独的方法去做的
没一个空格,通过横、纵、小区域来判断这个空格有哪几个可能性,如果只有一个可能,直接填入
如果删选发现所有的格子都是多种可能,那就选一个可能性少的(2选1),填入一个值后进行假设猜想,如果失败,换一个值测试
我就把我做题的逻辑,写成了代码
为了方便理解和自己写代码,加了两个类
NodeB:用于保存剩余值,比如某一行已经有了几个值,把剩余的值保存(使用在某一行、某一列、某一个3X3区域)
NodeC:格子,用于保存可能的可能值
代码:
public void solveSudoku(char[][] board) {
//每一横的剩余数
NodeB[] rNnodes = new NodeB[9];
//每一纵的剩余数
NodeB[] cNnodes = new NodeB[9];
//每一个小区域的剩余数
NodeB[] hNnodes = new NodeB[9];
for (int i = 0; i < 9; i++) {
rNnodes[i] = new NodeB();
cNnodes[i] = new NodeB();
hNnodes[i] = new NodeB();
}
Set<NodeC> nodeCs = new HashSet<>();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.') {
nodeCs.add(new NodeC(i, j));
} else {
int v = board[i][j] - '0';
//当一个格子有值时,横、纵、小区域的剩余数移除
rNnodes[i].remove(v);
cNnodes[j].remove(v);
hNnodes[(i / 3) * 3 + j / 3].remove(v);
}
}
}
while (true) {
Set<NodeC> removeSet = new HashSet<>();
for (NodeC nodeC : nodeCs) {
NodeB nr = rNnodes[nodeC.r];
NodeB nc = cNnodes[nodeC.c];
NodeB nh = hNnodes[(nodeC.r / 3) * 3 + nodeC.c / 3];
nodeC.setVs(nr, nc, nh);
if (nodeC.size == 1) {
//只有一个可能时,填入值,移除横、纵、小区域可能性
removeSet.add(nodeC);
board[nodeC.r][nodeC.c] = (char) (nodeC.vs[0] + '0');
nr.remove(nodeC.vs[0]);
nc.remove(nodeC.vs[0]);
nh.remove(nodeC.vs[0]);
}
}
nodeCs.removeAll(removeSet);
if (nodeCs.size() == 0) {
break;
}
if (removeSet.size() == 0) {
//循环一次,每一可填入值的时候,开始试
NodeC node = nodeCs.iterator().next();
nodeCs.remove(node);
for (int i = 0; i < node.size; i++) {
char[][] cc = doTest(copyOf(rNnodes), copyOf(cNnodes), copyOf(hNnodes), copyOf(board), copyOf(nodeCs), node.r, node.c, node.vs[i]);
if (cc != null) {
for (int k = 0; k < board.length; k++) {
for (int j = 0; j < board[0].length; j++) {
board[k][j] = cc[k][j];
}
}
}
}
return;
}
}
}
//假设操作
private char[][] doTest(NodeB[] rNnodes, NodeB[] cNnodes, NodeB[] hNnodes, char[][] board, Set<NodeC> nodeCs, int r, int c, int v) {
board[r][c] = (char) (v + '0');
rNnodes[r].remove(v);
cNnodes[c].remove(v);
hNnodes[(r / 3) * 3 + c / 3].remove(v);
while (true) {
Set<NodeC> removeSet = new HashSet<>();
for (NodeC nodeC : nodeCs) {
NodeB nr = rNnodes[nodeC.r];
NodeB nc = cNnodes[nodeC.c];
NodeB nh = hNnodes[(nodeC.r / 3) * 3 + nodeC.c / 3];
nodeC.setVs(nr, nc, nh);
if (nodeC.size == 1) {
removeSet.add(nodeC);
board[nodeC.r][nodeC.c] = (char) (nodeC.vs[0] + '0');
nr.remove(nodeC.vs[0]);
nc.remove(nodeC.vs[0]);
nh.remove(nodeC.vs[0]);
}
//每一可能性,说明假设失败
if (nodeC.size == 0) {
return null;
}
}
nodeCs.removeAll(removeSet);
if (nodeCs.size() == 0) {
return board;
}
if (removeSet.size() == 0) {
NodeC node = nodeCs.iterator().next();
nodeCs.remove(node);
for (int i = 0; i < node.size; i++) {
char[][] cc = doTest(copyOf(rNnodes), copyOf(cNnodes), copyOf(hNnodes), copyOf(board), copyOf(nodeCs), node.r, node.c, node.vs[i]);
if (cc != null) {
return cc;
}
}
//循环结束还是没有成功的,说明失败
return null;
}
}
}
private NodeB[] copyOf(NodeB[] rNnodes) {
NodeB[] mNodes = new NodeB[rNnodes.length];
for (int i = 0; i < rNnodes.length; i++) {
mNodes[i] = rNnodes[i].copy();
}
return mNodes;
}
private char[][] copyOf(char[][] board) {
char[][] nc = new char[board.length][board[0].length];
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
nc[i][j] = board[i][j];
}
}
return nc;
}
private Set<NodeC> copyOf(Set<NodeC> nodeCs) {
Set<NodeC> nNodeCs = new HashSet<>();
for (NodeC temp : nodeCs) {
nNodeCs.add(temp.copy());
}
return nNodeCs;
}
class NodeB {
int[] keys = new int[9];//剩余数数组
int size;//剩余数的个数
NodeB() {
for (int i = 0; i < 9; i++) {
keys[i] = i + 1;
}
size = 9;
}
public void remove(int v) {
for (int i = 0; i < size; i++) {
if (keys[i] == v) {
for (int j = i; j < size - 1; j++) {
keys[j] = keys[j + 1];
}
}
}
size--;
keys[size] = 0;
}
public NodeB copy() {
NodeB nNodeB = new NodeB();
nNodeB.size = size;
for (int i = 0; i < 9; i++) {
nNodeB.keys[i] = keys[i];
}
return nNodeB;
}
}
class NodeC {
int r;//格子的位置
int c;
int[] vs;//可能性的值
int size;//格子的可能性数
NodeC(int i, int j) {
r = i;
c = j;
}
public void setVs(NodeB nr, NodeB nc, NodeB nh) {
int[] kk = new int[10];
for (int i = 0; i < nr.size; i++) {
kk[nr.keys[i]]++;
}
for (int i = 0; i < nc.size; i++) {
kk[nc.keys[i]]++;
}
for (int i = 0; i < nh.size; i++) {
kk[nh.keys[i]]++;
}
vs = new int[9];
size = 0;
for (int i = 1; i <= 9; i++) {
if (kk[i] == 3) {
vs[size++] = i;
}
}
}
public NodeC copy() {
NodeC nNodeC = new NodeC(r, c);
nNodeC.vs = new int[9];
nNodeC.size = size;
for (int i = 0; i < size; i++) {
nNodeC.vs[i] = vs[i];
}
return nNodeC;
}
}
代码执行时间偏长,原因几点
为了方便理解,使用了新的类对象
每一次遍历都是循环,可以改成递归,如某一格修改之后,只需要再判断同行、同列、同区域的
不过,写这些代码就花了两个小时,有空再改吧
leetcode的一些已经写的觉得有意思的其他题目
https://blog.csdn.net/qq_33321609/article/category/9012437
如果有我没有写博客的其他题目需要讨论,欢迎评论,一起探讨