1. 题目描述
2. 解题思路
题意所示,有效的数独需要满足三个条件:行中无重复、列中无重复和3X3中无重复,因此我们可以用一个哈希表记录元素是否出现过,针对三种不同的情况分别遍历一次数独就可以得出答案了,一共遍历三次,但是这样做比较浪费时间,可以遍历两次就得出答案吗?
观察一下发现,其实行的重复判断与列的重复判断可以在一次遍历中同时实现的,只需要把二维数组的下标互换就可以了,这样就需要两个哈希表用于记录元素是否出现过,一个记录行,一个记录列。
既然行与列的判断可以合并在一次遍历中,那3X3的判断可以合并吗?其实也是可以的,只要能找出下标的对应规律就可以了,这样就需要三个哈希表用于记录元素是否出现过,其下标的对应规律如下:
- a = (i / 3) * 3 + j / 3
- b = (i % 3) * 3 + j % 3
从三次遍历优化到一次遍历了,还有什么地方可以优化的吗?看过我文章的朋友应该知道,之前提到过好几次在条件允许的情况下,采用数组代替哈希表可以提高运行效率,同样的这里也可以使用三个数组代替三个哈希表用于储存记忆。
3. 代码实现
3.1 哈希表
public boolean isValidSudoku(char[][] board) {
for (int i = 0; i < 9; i++) {
HashSet<Character> setLine = new HashSet<>();
HashSet<Character> setColumn = new HashSet<>();
HashSet<Character> setCube = new HashSet<>();
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.' && !setLine.add(board[i][j])) {
return false;
}
if (board[j][i] != '.' && !setColumn.add(board[j][i])) {
return false;
}
int a = (i / 3) * 3 + j / 3;
int b = (i % 3) * 3 + j % 3;
if (board[a][b] != '.' && !setCube.add(board[a][b])) {
return false;
}
}
}
return true;
}
3.2 数组
public boolean isValidSudoku(char[][] board) {
for (int i = 0; i < 9; i++) {
int[] line = new int[9];
int[] column = new int[9];
int[] cube = new int[9];
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.' && ++line[board[i][j] - '1'] > 1) {
return false;
}
if (board[j][i] != '.' && ++column[board[j][i] - '1'] > 1) {
return false;
}
int a = (i / 3) * 3 + j / 3;
int b = (i % 3) * 3 + j % 3;
if (board[a][b] != '.' && ++cube[board[a][b] - '1'] > 1) {
return false;
}
}
}
return true;
}
3.3 对比
两种算法的时间复杂度和空间复杂度都是O(1),一共只需要遍历81次,存储27个变量(不算循环变量)。