• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
有顶天の甲子园
博客园    首页    新随笔    联系   管理    订阅  订阅
【LeetCode】数独

判断一个数独是否合法,未填的空格用字符 ' . ' 表示。该数独有解并不是必要的。

e.g. 如图合法数独,输入

["53..7....","6..195...",".98....6.","8...6...3","4..8.3..1","7...2...6",".6....28.","...419..5","....8..79"]

返回 true。

 

我依然使用死办法解决,而且进行了输入合法性判断,即必须为 1 ~ 9 的数字或 ' . ' 。

 1 bool isValidSudoku(vector<vector<char>>& board) {
 2     vector<char> row, column, subbox;
 3     for (int i = 0; i < 9; ++i) {
 4         row.clear();
 5         column.clear();
 6         row = board[i];
 7         if (!isValid(row))
 8             return false;
 9         for (int j = 0; j < 9; ++j) {
10             column.push_back(board[j][i]);
11         }
12         if (!isValid(column))
13             return false;
14     }
15     for (int i = 0; i <= 6; i = i + 3) {
16         for (int j = 0; j <= 6; j = j + 3) {
17             subbox.clear();
18             subbox.push_back(board[i][j]);    subbox.push_back(board[i][j+1]);    subbox.push_back(board[i][j+2]);
19             subbox.push_back(board[i+1][j]);  subbox.push_back(board[i+1][j+1]);  subbox.push_back(board[i+1][j+2]);
20             subbox.push_back(board[i+2][j]);  subbox.push_back(board[i+2][j+1]);  subbox.push_back(board[i+2][j+2]);
21             if (!isValid(subbox))
22                 return false;
23         }
24     }
25     return true;
26 }
27     
28     bool isValid(vector<char> &t) {
29     sort(t.begin(), t.end());
30     for (int i = 0; i < 9; ++i) {
31         if (((t[i] < '1' || t[i] > '9') && t[i] != '.') || (i > 0 && t[i] == t[i - 1] && t[i] != '.'))
32             return false;
33     }
34     return true;
35 }

 

    答案巧妙的做法如下

 1 bool isValidSudoku(vector<vector<char>>& board) {
 2     bool row[9][9] = {false}, col[9][9] = {false}, box[9][9] = {false};
 3     for (int i = 0; i < 9; i++) {
 4         for (int j = 0; j < 9; j++) {
 5             if (board[i][j] != '.') {
 6                 int num = board[i][j] - '0' - 1, k = i / 3 * 3 + j / 3;
 7                 if (row[i][num] || col[j][num] || box[k][num]) return false;
 8                 row[i][num] = col[j][num] = box[k][num] = true;
 9             }
10         }
11     }
12     return true;
13 }

num = board[i][j] - '0' - 1 的思路就是用一个新 bool 型数组(初始化全为 false)判断原数组是否有重复元素。

k = i / 3 * 3 + j / 3 将原来 9 * 9 的方格映射到 3 * 3 的方格中!

0 | 1 | 2
3 | 4 | 5
6 | 7 | 8

例如 i = 5,j = 6 (第 5 行 第 6 列)时,k = 5 / 3 * 3 + 6 / 3 = 1 * 3 + 2 = 5,在 box[5][] 这个数组里进行判断。

这种方法更常规的用法见下。

 

    这个Java实现也很巧妙

 1 public boolean isValidSudoku(char[][] board) {
 2     for(int i = 0; i < 9; i++) {
 3         Set<Character> rows = new HashSet<>();
 4         Set<Character> cols = new HashSet<>();
 5         Set<Character> cubes = new HashSet<>();            
 6         for (int j = 0; j < 9; j++) {
 7             if (board[i][j] != '.' && !rows.add(board[i][j])) return false;
 8             if (board[j][i] != '.' && !cols.add(board[j][i])) return false;
 9             int colStart = 3 * (i % 3), rowStart = 3 * (i / 3);
10             int colOffset = j % 3, rowOffset = j / 3;   // 偏移
11             int row = rowStart + rowOffset, col = colStart + colOffset;
12             if (board[row][col] != '.' && !cubes.add(board[row][col]) ) return false;
13         }
14     }
15     return true;
16 }

HashSet 的 add(E e) 方法用于将指定元素添加到这个 HashSet,若此 Set 已经包含该元素,则直接返回 false。

 

% 和 / 操作符对于矩阵遍历问题很有帮助。

使用 % 作水平遍历,即计算列坐标偏移。因为 j 每增加 1,j % 3 也增加 1 然后重置。

使用  /  作竖直遍历,即计算行坐标偏移。因为 j 每增加 3,j / 3 才能增加 1。

通过 0 ~ 8 的 j 即可遍历一个 9 * 9 矩阵的一个 3 * 3 子块。如何继续遍历下一个子块呢?就需要用外层循环 0 ~ 8 的 i 实现。

依然使用 % 水平遍历到下一个子块,colStart = 3 * (i % 3) ,× 3 是因为下一个子块在 3 列之后,第一个子块的起始是 (0, 0),第二个子块的起始是 (0, 3) 而不是 (0, 1)。

e.g.

i = 2 时,j 从 0 ~ 8,

rowStart = 3 * (2 / 3) = 0                                      colStart = 3 * (2 % 3) = 6

rowOffset = j / 3 = 0,0,0, 1,1,1, 2,2,2                   colOffset = j % 3 = 0,1,2, 0,1,2, 0,1,2

对应了 board 矩阵中的

(0+0, 6+0) (0+0, 6+1) (0+0, 6+2)
(0+1, 6+0) (0+1, 6+1) (0+1, 6+2)
(0+2, 6+0) (0+2, 6+1) (0+2, 6+2)

即

(0,6) (0,7) (0,8)
(1,6) (1,7) (1,8)
(2,6) (2,7) (2,8)

这个 Sub-Box。

 

posted on 2017-07-18 11:28  生涯现役丶  阅读(200)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3