跳至主要內容

Mr.He大约 2 分钟

这是一个经典的算法题,我们可以通过一次遍历来解决这个问题。

解题思路

我们需要验证数独的三个规则:

  1. 行规则:每一行只能出现一次数字 1-9。
  2. 列规则:每一列只能出现一次数字 1-9。
  3. 宫规则:每一个 3x3 的小宫格内只能出现一次数字 1-9。 我们可以使用 哈希表(或数组) 来记录数字的出现情况。具体来说,我们需要为行、列和宫各维护一组记录。 由于数字范围只是 1-9,使用数组来记录是最快速的(虽然使用 Set 也可以,但数组效率更高)。 关于 3x3 宫格的索引计算: 这是本题的关键点。对于一个坐标 (row, col),它属于哪个 3x3 宫格呢? 计算公式为:boxIndex = (row / 3) * 3 + (col / 3)(整数除法)。 例如:
  • (0, 0) -> 第 0 个宫
  • (0, 4) -> 第 1 个宫
  • (4, 4) -> 第 4 个宫

JavaScript 代码实现

/**
 * @param {character[][]} board
 * @return {boolean}
 */
var isValidSudoku = function(board) {
    // 初始化记录数组
    // rows[i][num] 表示第 i 行是否出现过数字 num
    const rows = Array.from({ length: 9 }, () => Array(10).fill(false)); // 索引 1-9,0 位置不用
    // cols[j][num] 表示第 j 列是否出现过数字 num
    const cols = Array.from({ length: 9 }, () => Array(10).fill(false));
    // boxes[k][num] 表示第 k 个 3x3 宫格是否出现过数字 num
    const boxes = Array.from({ length: 9 }, () => Array(10).fill(false));
    for (let i = 0; i < 9; i++) {
        for (let j = 0; j < 9; j++) {
            const val = board[i][j];
            
            // 如果是空格 '.',直接跳过
            if (val === '.') continue;
            // 将字符数字转为整数 (例如 '5' -> 5)
            const num = parseInt(val);
            
            // 计算当前属于第几个 3x3 宫格
            const boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3);
            // 检查是否已经存在
            if (rows[i][num] || cols[j][num] || boxes[boxIndex][num]) {
                // 如果在行、列或宫格中已经记录过该数字,说明重复,返回 false
                return false;
            }
            // 标记该数字已出现
            rows[i][num] = true;
            cols[j][num] = true;
            boxes[boxIndex][num] = true;
        }
    }
    // 遍历结束,未发现重复,返回 true
    return true;
};

复杂度分析

  • 时间复杂度:$O(1)$。因为数独的大小固定为 9x9,我们需要遍历 81 个格子,操作次数是固定的常数,不随输入规模变化。
  • 空间复杂度:$O(1)$。我们使用了三个 9x10 的二维数组来存储记录,空间占用也是固定的常数。