Code Monkey home page Code Monkey logo

Comments (2)

domliang avatar domliang commented on July 26, 2024
func solveSudoku(board [][]byte) {
    //统计每行/列/块已经使用的数字,按位存储
    var rows, cols, zones = make([]int, 9), make([]int, 9), make([]int, 9)
    for i := range board {
        for j := range board[i] {
            if board[i][j] != '.' {
                var bit = 1 << (board[i][j] - '0')
                rows[i] |= bit
                cols[j] |= bit
                zones[i/3*3+j/3] |= bit
            }
        }
    }
    dfs(board, 0, rows, cols, zones)
}

//将二维数组按行打平为一维数组, 按一维索引位置递归填充数字, 这里之所以打平为一维数组,避免递归时重复检查之前的元素
//n: 按行打平为一维数组时的索引位置
//rows: 遍历第n个位置时,各行已使用的数字集合
//cols: 遍历第n个位置时,各列已使用的数字集合
//zones: 遍历第n个位置时,各列已使用的数字集合
func dfs(board [][]byte, n int, rows []int, cols []int, zones []int) bool {
    //第n个位置填充完成,递归结束
    if n == len(board)*len(board) {
        return true
    }
    //定位行与列
    var i, j = n / len(board), n % len(board)
    //当前位置已填数字,直接判断下一个位置
    if board[i][j] != '.' {
        return dfs(board, n+1, rows, cols, zones)
    }
    //当前位置未填充数字,则尝试填充数字1~9
    for k := 1; k <= 9; k++ {
        var bit = 1 << k
        //判断所在行/列/块是否使用该数字
        if unused := rows[i]&bit == 0 && cols[j]&bit == 0 && zones[i/3*3+j/3]&bit == 0; unused {
            //对应行/列/块标记该数字被占用
            rows[i] |= bit
            cols[j] |= bit
            zones[i/3*3+j/3] |= bit
            //继续填充下一个位置
            if dfs(board, n+1, rows, cols, zones) {
                //这里递归返回true,表示后续位置都已经正确填充,此时可以填充当前位置
                board[i][j] = uint8(k + '0')
                return true
            } else {
                //这里递归返回false,表示后续位置填充失败,因此当前数字无效,我们需要尝试下一个数字
                //在尝试下一个数字前,我们要移除对应行/列/块所标记的当前数字
                rows[i] &= ^bit
                cols[j] &= ^bit
                zones[i/3*3+j/3] &= ^bit
            }
        }
    }
    //此处表示该位置1~9均不可填充, 数独无解
    return false
}

from daily-leetcode.

domliang avatar domliang commented on July 26, 2024

第一次解数独问题,基本思路就是递归, 首先找出所有空格子以及初始可能填充的数字,依次尝试。因为填充可能失败,因此递归退出时需要恢复当前状态,这就是回溯。

大家的思路差不多,但是在具体实现细节上各有差别, 这里提供一个非常简洁的思路。

首先, 我看很多人递归时是基于二维数组的递归,在每一层递归时,都要从头检查空格,实际上没有必要。假设当前检查第i行第j列的空格,尝试填充数字后,递归检查下一个空格,应该直接从第i行j+1列或者i+1行0列继续检查。这里之所以递归从头检查不会进入死循环,是因为递归时第i行j列位置的空格已经被填充,所以即使从头寻找下一个空格,也是在其后的空格,但是实际遍历二维的操作是冗余的。 那么有没有更好的办法呢? 答案是有的, 就是我们可以将二维数组打平成一维数组进行递归。 对于9*9的二维数组,按照行优先的访问顺序,81个格子对应的索引为0~80, 假设当前索引为n,则对应的行为n/9,列为n%9,这样递归时只需n+1就可以了。

其次,在判断当前位置数字是否有效时,一般有两种思路,一种思路是看数独空格填充数字之后,是否满足要求,这时需要检查行、列以及子块,好处是不需要额外申请空间,坏处时,没有办法优化检查效率。另一种思路时,记录各行、列、子块已经使用的数字,判断当前数字是否已经被占用,好处是可以利用各种方法优化检查效率,坏处是需要额外申请空间。其实,这里还有另一个好处,不需要原二维数组记录临时结果。

再次,上面我们提到检查填充数字是否有效时的优化手段,由于每个空格只能填充1~9九个数字,显然利用位运算可以将二维数组降维成一维数组。我们可以使用三个一维数组分别记录各行、列以及子块已经使用的数字,同时利用位运算的高效进行数字判断。

这里,我们首先将已经填充的数字按照行、列以及子块的顺序进行记录。然后在尝试填充某个空格时,我们会先判断该数字是否被空格所在行、列、子块中被占用,如果没有被占用,则所在行、列以及子块登记该数字,继续填充下一个空格。如果后续填充失败,则需要在行、列以及子块中移除该数字,尝试下一个数字。
思路已经很清晰,代码非常简洁,相信大家一看就懂。

from daily-leetcode.

Related Issues (10)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.