每天一道LeetCode-----数独盘求解
2017-11-05 14:51
369 查看
Valid Sudoku
原题链接Valid Sudoku判断给定的数独盘是否有效,数独盘中可能有空位置。
简述一下数独的规则,参考连接Sudoku Puzzles - The Rules.
每一行,数字1-9只能出现一次
每一列,数字1-9只能出现一次
每个3 * 3方格中,数字1-9只能出现一次。这里3 * 3方格只包括9个,即图片中加粗黑线分开的9个方格
其实就是每个点只能存在1-9这九个数字中的一个,满足每一行,每一列,每个3 * 3方格不能出现重复的数字
判断一个数独盘是否有效,只需要判断是否满足上述三个规则即可。另外,如果给出的数独盘像图片那样有些地方没有填充数字,也没有关系。只需要判断有数字的部分就好,比如说第一行只有5, 3, 7,那么可以判断第一行没有出现重复的数字,是满足规则一的。
肯定需要全部遍历一遍,每遍历到一个位置,就判断它所在的行,所在的列,所在的3 * 3方格是否已经存在同样的数字了,如果存在,返回false,否则,将这个数字添加到行,列,3 * 3方格的记录中。
所以需要分别记录每一行,每一列,每一个3 * 3方格都有那些数字出现过,其实就是3个二维数组
记录每一行都出现了哪些数字,
vector<vector<int>> rows(9, vector<int>(10, 0));
记录每一列都出现了哪些数字,
vector<vector<int>> columns(9, vector<int>(10, 0));
记录每个3 * 3方格都出现了哪些数字,
vector<vector<int>> boxes(9, vector<int>(10, 0));
对于3 * 3方格,这里用
vector<vector<int>> boxes(9, vector<int>(10, 0));形式表示,意思是这个数独盘是由9个3 * 3方格,编号从0到8。对于某个位置(row, column)而言,它所在的3 * 3方格编号为
row / 3 * 3 + column / 3。
表示方法,假设当前位置为(i, j),数字为n
rows[i]
表示第i行出现数字n的个数,不是1就是0
columns[j]
表示第j列出现数字n的个数,不是1就是0
boxes[i / 3 * 3 + j / 3]
表示当前位置所在的方格出现数字n的个数,不是1就是0
代码如下
class Solution { public: bool isValidSudoku(vector<vector<char>>& board) { vector<vector<int>> rows(9, vector<int>(10, 0)); vector<vector<int>> columns(9, vector<int>(10, 0)); vector<vector<int>> boxes(9, vector<int>(10, 0)); for(int i = 0; i < board.size(); ++i) { for(int j = 0; j < board[i].size(); ++j) { if(board[i][j] == '.') continue; int n = board[i][j] - '0'; /* 如果之前有出现过(不为0),就说明数独盘无效 */ if(rows[i] || columns[j] || boxes[i / 3 * 3 + j / 3] ) return false; /* 否则,更新每一行,每一列,所在方格的内容 */ else rows[i] = columns[j] = boxes[i / 3 * 3 + j / 3] = 1; } } return true; } };
扩展
Sudoku Solver
原题链接Sudoku Solver给定一个有效的数独盘,解出结果。
解一个数独盘就是要求把所有的空格都填上数字,要求仍然是满足上述三个规则,即
每一行,数字1-9只能出现一次
每一列,数字1-9只能出现一次
每个3 * 3方格中,数字1-9只能出现一次。这里3 * 3方格只包括9个,即图片中加粗黑线分开的9个方格
对于某个空格,它所能填充的数字需要满足
在所在行没有出现过的
在所在行没有出现过的
在所在3 * 3方格没有出现过的
所以在上面的问题中,已经把每一行,每一列,每个3 * 3方格出现的数字都找出了,接下来就是深度优先(dfs)把每个空格填上数字即可,当然填充的数字需要满足上面的要求。
如果填充到某个位置发现没有可选的数字了,就说明之前的某个位置选择错了,就回退到之前的位置,选择另一个满足上述要求的数字
代码如下
class Solution { public: void solveSudoku(vector<vector<char>>& board) { vector<vector<int>> rows(9, vector<int>(10, 0)); vector<vector<int>> columns(9, vector<int>(10, 0)); vector<vector<int>> boxes(9, vector<int>(10, 0)); /* 计算每一行,每一列,每个3 * 3方格中每个数字是否出现 */ for(int i = 0; i < board.size(); ++i) { for(int j = 0; j < board[i].size(); ++j) { if(board[i][j] == '.') continue; int n = board[i][j] - '0'; rows[i] = columns[j] = boxes[i / 3 * 3 + j / 3] = 1; } } bool done = false; dfs(board, rows, columns, boxes, 0, 0, done); } private: void dfs(vector<vector<char>>& board, vector<vector<int>>& rows, vector<vector<int>>& columns, vector<vector<int>>& boxes, int row, int column, bool& done) { /* 填充完成 */ if(row >= board.size()) { done = true; return; } /* 当前某一行的末尾,换到下一行 */ else if(column >= board[row].size()) { dfs(board, rows, columns, boxes, row + 1, 0, done); } /* 如果有数字,则不需要填充,继续下一个 */ else if(board[row][column] != '.') { dfs(board, rows, columns, boxes, row, column + 1, done); } else { for(int n = 1; n <= 9; ++n) { /* 如果数字出现过,就不能填充到当前位置 */ if(rows[row] || columns[column] || boxes[row / 3 * 3 + column / 3] ) continue; /* 将填充的数字记录下来 */ rows[row] = columns[column] = boxes[row / 3 * 3 + column / 3] = 1; board[row][column] = n + '0'; /* 递归填充下一个空格 */ dfs(board, rows, columns, boxes, row, column + 1, done); /* 如果完成,就退出 */ if(done) { return; } /* 否则,回到填充之前的状态,重新找数字 */ else { rows[row] = columns[column] = boxes[row / 3 * 3 + column / 3] = 0; board[row][column] = '.'; } } } } };
相关文章推荐
- 每天一道LeetCode-----重新实现next_permutation
- 每天一道LeetCode-----寻找地增序列中第一个大于等于目标元素的位置
- 每天一道LeetCode-----判断两个二叉树是否相同
- 每天一道LeetCode--326. Power of Three
- 每天一道LeetCode-----两个有序数组合并后的第K个数
- 每天一道LeetCode-----在有序的二维数组中查找某个元素
- 每天一道LeetCode-----给定字符串s和字符数组words,在s中找到words出现的位置,words内部字符串顺序无要求
- 每天一道LeetCode-----根据先序遍历和中序遍历还原二叉树
- 每天一道LeetCode-----数组序列,每个元素的值表示最多可以向后跳多远,计算最少跳多少次可以到达末尾
- 每天一道LeetCode-----计算给定范围内所有数的与运算结果
- 每天一道LeetCode-----找到给定数组的连续子数组,使这个子数组的和最大,要求复杂度为O(n)
- 每天一道LeetCode--344. Reverse String
- 每天一道LeetCode-----最长无重复子串
- 每天一道LeetCode-----在字符串s中找到最短的包含字符串t中所有字符的子串,子串中字符顺序无要求且可以有其他字符
- 每天一道LeetCode-----平面木桶最大容量,以较小的纵坐标为高,横坐标差为底
- 每天一道LeetCode-----实现LRU置换算法
- 每天一道LeetCode-----在给定序列中找到满足nums[i]>nums[i-1]&&nums[i]>nums[i+1]的位置,要求时间复杂度是O(logN)
- 每天一道LeetCode-----实现二叉搜索树的迭代器
- 每天一道题:LeetCode
- 每天一道LeetCode-----有序数组右移n位后查找某个元素