39- 有效的数独
请你判断一个 9 x 9
的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
- 数字
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"]] 输出:true
示例 2:
输入:board = [["8","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"]] 输出:false 解释:除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
提示:
board.length == 9
board[i].length == 9
board[i][j]
是一位数字(1-9
)或者'.'
方法一:分别检查行、列和 3x3 宫格
此方法依次对每一行、每一列以及每一个 3x3 宫格进行检查,利用集合来记录已出现的数字,若发现重复数字则判定数独无效。
function isValidSudoku(board: string[][]): boolean {// 检查每一行for (let i = 0; i < 9; i++) {const rowSet = new Set<string>();for (let j = 0; j < 9; j++) {const cell = board[i][j];if (cell!== '.') {if (rowSet.has(cell)) {return false;}rowSet.add(cell);}}}// 检查每一列for (let j = 0; j < 9; j++) {const colSet = new Set<string>();for (let i = 0; i < 9; i++) {const cell = board[i][j];if (cell!== '.') {if (colSet.has(cell)) {return false;}colSet.add(cell);}}}// 检查每一个 3x3 宫格for (let i = 0; i < 9; i += 3) {for (let j = 0; j < 9; j += 3) {const boxSet = new Set<string>();for (let x = i; x < i + 3; x++) {for (let y = j; y < j + 3; y++) {const cell = board[x][y];if (cell!== '.') {if (boxSet.has(cell)) {return false;}boxSet.add(cell);}}}}}return true;
}
复杂度分析
- 时间复杂度:(O(1)),由于数独的规模固定为 9x9,代码需遍历 81 个单元格,操作次数恒定。
- 空间复杂度:(O(1)),在检查行、列和 3x3 宫格时,集合最多存储 9 个元素,属于常数级空间复杂度。
方法二:一次遍历检查
该方法在一次遍历数独的过程中,同时记录每一行、每一列以及每一个 3x3 宫格中数字的出现情况,使用三个二维数组来分别存储这些信息。
function isValidSudoku(board: string[][]): boolean {const rows: boolean[][] = Array.from({ length: 9 }, () => Array(9).fill(false));const cols: boolean[][] = Array.from({ length: 9 }, () => Array(9).fill(false));const boxes: boolean[][] = Array.from({ length: 9 }, () => Array(9).fill(false));for (let i = 0; i < 9; i++) {for (let j = 0; j < 9; j++) {const cell = board[i][j];if (cell!== '.') {const num = parseInt(cell) - 1;const boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3);if (rows[i][num] || cols[j][num] || boxes[boxIndex][num]) {return false;}rows[i][num] = true;cols[j][num] = true;boxes[boxIndex][num] = true;}}}return true;
}
复杂度分析
- 时间复杂度:(O(1)),同样因为数独大小固定,仅需遍历 81 个单元格。
- 空间复杂度:(O(1)),使用的三个二维数组大小均为 9x9,是常数级的空间开销。
方法三:使用哈希表优化
通过使用哈希表来存储每一行、每一列和每一个 3x3 宫格中数字的出现次数,在遍历过程中更新哈希表并检查是否有重复数字。
function isValidSudoku(board: string[][]): boolean {const rows: Map<number, Set<string>> = new Map();const cols: Map<number, Set<string>> = new Map();const boxes: Map<number, Set<string>> = new Map();for (let i = 0; i < 9; i++) {rows.set(i, new Set());cols.set(i, new Set());boxes.set(i, new Set());}for (let i = 0; i < 9; i++) {for (let j = 0; j < 9; j++) {const cell = board[i][j];if (cell!== '.') {const boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3);if (rows.get(i)!.has(cell) || cols.get(j)!.has(cell) || boxes.get(boxIndex)!.has(cell)) {return false;}rows.get(i)!.add(cell);cols.get(j)!.add(cell);boxes.get(boxIndex)!.add(cell);}}}return true;
}
复杂度分析
- 时间复杂度:(O(1)),遍历 9x9 的数独网格,操作次数固定。
- 空间复杂度:(O(1)),哈希表最多存储 9 行、9 列和 9 个 3x3 宫格的信息,每个集合最多存储 9 个元素,为常数级空间复杂度。
你可以使用以下代码测试上述函数:
const board1 = [["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"]
];
console.log(isValidSudoku(board1));const board2 = [["8", "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"]
];
console.log(isValidSudoku(board2));