【LeetCode】36. 有效的数独

leetcode

 

解题思路

要验证一个 9x9 数独的有效性,需要同时满足三个条件:

  1. ​​行内无重复​​:每行中数字 1-9 最多出现一次
  2. ​​列内无重复​​:每列中数字 1-9 最多出现一次
  3. ​​宫内无重复​​:每个 3x3 宫内数字 1-9 最多出现一次

通过​​一次遍历​​即可完成所有验证,使用三个辅助数据结构分别记录行、列、宫的数字出现情况,确保时间复杂度最优。

 

关键步骤

  1. ​​数据结构初始化​​:

    • 创建行记录器 rows:二维布尔数组,rows[i][num] 表示第 i 行数字 num 是否出现
    • 创建列记录器 cols:二维布尔数组,cols[j][num] 表示第 j 列数字 num 是否出现
    • 创建宫记录器 boxes:二维布尔数组,boxes[k][num] 表示第 k 个宫数字 num 是否出现
  2. ​​宫索引计算​​:

    • 使用公式 boxIndex = (i/3)*3 + j/3 确定单元格所属宫
    • 例如位置 (4,5)(4/3)=1 → 1 * 3=35/3=1 → 3+1=4(第 5 个宫)
  3. ​​遍历验证​​:

    • 遍历每个单元格,跳过空白格('.')
    • 若数字在行、列或宫中已记录,返回无效
    • 否则更新三个记录器

代码实现

func isValidSudoku(board [][]byte) bool {
    // 初始化记录器(行、列、宫)
    rows := make([][]bool, 9)
    cols := make([][]bool, 9)
    boxes := make([][]bool, 9)

    for i := 0; i < 9; i++ {
        rows[i] = make([]bool, 10) // 数字0-9(索引0不用)
        cols[i] = make([]bool, 10)
        boxes[i] = make([]bool, 10)
    }

    for i := 0; i < 9; i++ {
        for j := 0; j < 9; j++ {
            // 跳过空白单元格
            if board[i][j] == '.' {
                continue
            }

            // 字符转数字(‘5’->5)
            num := board[i][j] - '0'

            // 计算宫索引(核心公式)
            boxIndex := (i/3)*3 + 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
}

示例测试

func main() {
    // 示例1(有效)
    board1 := [][]byte{
        {'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'},
    }
    fmt.Println("示例1:", isValidSudoku(board1)) // true

    // 示例2(无效:左上宫重复8)
    board2 := [][]byte{
        {'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'},
    }
    fmt.Println("示例2:", isValidSudoku(board2)) // false
}

复杂度分析

  • ​​时间复杂度​​:O(1)
    固定遍历 81 个单元格(9x9 大小恒定)
  • ​​空间复杂度​​:O(1)
    使用固定空间:3 个 9x10 的布尔数组(总计 270 字节)

关键点总结

    1. ​​宫索引计算​​:
      公式 (i/3)*3 + j/3 将二维坐标映射到 0-8 的宫编号
    2. ​​一次遍历三验证​​:
      单次遍历同时检查行、列、宫,避免多次扫描
    3. ​​字符转换技巧​​:
      byte('5') - '0' = 5 快速将字符转为数字索引
    4. ​​部分填充验证​​:
      仅检查已填数字,不要求数独可解
    5. ​​布尔数组优化​​:
      使用布尔值而非计数器,节省空间且避免整数运算
posted @ 2025-06-13 11:41  云隙之间  阅读(27)  评论(0)    收藏  举报