LC-37. 解数独

103 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图) 数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

image.png

输入: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"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:

image.png

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12

提示:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] 是一位数字或者 '.'
  • 题目数据 保证 输入数独仅有一个解

题解

这种棋盘类的题,都是可以通过 回溯 的一个方式来解决。

这道题的难点,在于 return 的时机,需要想明白 何时进行 return,值 true 或是 false

判断棋盘是否出现重复,则判断当前行,当前列,当前3*3 网格内是否存在这个值 即可。

回溯

const solveSudoku = (board) => {
  const boardRowLength = board.length
  const boardColumnLength = board[0].length

  const isRepeat = (x, y, value) => {
    // 判断当前行是否存在
    if (board[x].indexOf(value) !== -1) return true

    // 判断当前列是否存在
    for (let i = 0; i < boardRowLength; i++) {
      if (board[i][y] === value) {
        return true
      }
    }

    // 判断3*3网格是否存在
    const startX = Math.floor(x / 3) * 3
    const startY = Math.floor(y / 3) * 3
    const endX = startX + 3
    const endY = startY + 3

    for (let i = startX; i < endX; i++) {
      for (let j = startY; j < endY; j++) {
        if (board[i][j] === value) {
          return true
        }
      }
    }

    return false
  }

  const search = () => {
    for (let i = 0; i < boardRowLength; i++) {
      for (let j = 0; j < boardColumnLength; j++) {
        // 先判断 是否为 .
        if (board[i][j] !== '.') continue

        // 如果为点 则进行遍历
        for (let val = 1; val <= 9; val++) {
          // 判断是否重复
          if (!isRepeat(i, j, `${val}`)) {
            // 不重复
            board[i][j] = `${val}`

            // 进行递归 操作,如果找到,则返回true,代表找到了
            if (search()) {
              return true
            }
            board[i][j] = '.'
          }
        }

        // 如果 1~9 ,这几个数都不可以的话,就返回false
        return false
      }
    }
    
    // 如果遍历完后,没有发现false,那么就返回true,说明已经找到符合的棋盘
    return true
  }

  search()

  return board
}

总结

题目 29 :回溯,可以很清楚的解决我们的棋盘类问题,只要不符合,我们就回退即可,直到找到我们要的结果。