【leetcode】37. Sudoku Solver

本文详细介绍了如何使用回溯法解决数独问题,通过逐个单元格填充数字并检查行、列及3x3子网格的有效性,实现了数独求解器。文章提供了两种不同的C++代码实现,一种直接在原始数据上进行判断,另一种优化了回溯过程,避免重复遍历已知单元格。

37. Sudoku Solver

Write a program to solve a Sudoku puzzle by filling the empty cells.

A sudoku solution must satisfy all of the following rules:

  1. Each of the digits 1-9 must occur exactly once in each row.
  2. Each of the digits 1-9 must occur exactly once in each column.
  3. Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.

Empty cells are indicated by the character '.'.


A sudoku puzzle...


...and its solution numbers marked in red.

Note:

  • The given board contain only digits 1-9 and the character '.'.
  • You may assume that the given Sudoku puzzle will have a single unique solution.
  • The given board size is always 9x9.

题目链接:https://leetcode.com/problems/sudoku-solver/

心路历程

刚看到题目的思路很复杂,想着能联系上一道题,一边记录遍历,一边通过判断在记录里是否符合规则而选择数字。

事实证明果然想复杂了,复杂度高,还不可行。

直接带入原数据里去挨个判断就好。

方法:回溯法

class Solution {
public:
    void solveSudoku(vector<vector<char>>& board) {
        fill(board);
    }
    int fill(vector<vector<char>>& board){
        for(int i=0; i<9; ++i){
            for(int j=0; j<9; ++j){
                int num = 1;
                if(board[i][j]-'0' == '.'-'0'){
                    while(num<=9){
                        if(isValid(board, i, j, num)){
                            board[i][j] = '0'+num;
                            if(fill(board)){
                                return true;
                            }else{
                                board[i][j] = '.';
                            }
                        }
                        ++num;
                    }
                    return false;
                }
            }
        }
        return true;
    }
    bool isValid(vector<vector<char>>& board, int i, int j, int num){
        for (int l=0; l<9; ++l){
            if(board[i][l]-'0'==num) return false;
            if(board[l][j]-'0'==num) return false;
        }
        int startL = int(i/3) *3;
        int startC = int(j/3) *3;
        for (int l = startL; l<startL+3; ++l){
            for(int c = startC; c<startC+3; ++c){
                if(board[l][c]-'0'==num)return false;
            }
        }
        return true;
    }
};

在运行时间上优化

记录当前遍历得到的位置,则在回溯的时候能直接跳到该位置,而不用重新再遍历一次前面已经遍历过的元素。

以下代码来自网友:https://www.cnblogs.com/ganganloveu/p/3828401.html

class Solution {
public:
    void solveSudoku(vector<vector<char> > &board) {
        solve(board, 0);
    }
    bool solve(vector<vector<char> > &board, int position)
    {
        if(position == 81)
            return true;

        int row = position / 9;
        int col = position % 9;
        if(board[row][col] == '.')
        {
            for(int i = 1; i <= 9; i ++)
            {//try each digit
                board[row][col] = i + '0';
                if(check(board, position))
                    if(solve(board, position + 1))
                    //only return valid filling
                        return true;
                board[row][col] = '.';
            }
        }
        else
        {
            if(solve(board, position + 1))
            //only return valid filling
                return true;
        }
        return false;
    }
    bool check(vector<vector<char> > &board, int position)
    {
        int row = position / 9;
        int col = position % 9;
        int gid;
        if(row >= 0 && row <= 2)
        {
            if(col >= 0 && col <= 2)
                gid = 0;
            else if(col >= 3 && col <= 5)
                gid = 1;
            else
                gid = 2;
        }
        else if(row >= 3 && row <= 5)
        {
            if(col >= 0 && col <= 2)
                gid = 3;
            else if(col >= 3 && col <= 5)
                gid = 4;
            else
                gid = 5;
        }
        else
        {
            if(col >= 0 && col <= 2)
                gid = 6;
            else if(col >= 3 && col <= 5)
                gid = 7;
            else
                gid = 8;
        }

        //check row, col, subgrid
        for(int i = 0; i < 9; i ++)
        {
            //check row
            if(i != col && board[row][i] == board[row][col])
                return false;
            
            //check col
            if(i != row && board[i][col] == board[row][col])
                return false;
            
            //check subgrid
            int r = gid/3*3+i/3;
            int c = gid%3*3+i%3;
            if((r != row || c != col) && board[r][c] == board[row][col])
                return false;
        }
        return true;
    }
};

ps.事实证明对回溯法、递归还不熟练。

todo:看视频加强

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值