[LeetCode 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:

Each of the digits 1-9 must occur exactly once in each row.
Each of the digits 1-9 must occur exactly once in each column.
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.

思路

  1. 先遍历,记录需要填数字的坐标和各行、各列、各小格子已经被填好的数字,考虑到代码清晰度,坐标用结构Slot{x, y}来表示,所有的空格用数组slots记录。考虑空间利用效率和随后的计算效率,每个数字用一个bit位表示,用三个数组就可以表示各行、各列、各小格子已经填好的数字,这里可以理解为数独的初始状态;
  2. 遍历slots,当前slot可填的数字(通过位操作即可得到可填入的数字集合,用bit来表示可填入的数字,一个整形即可以表示该集合)。将当前该slot所在行、列、小格子的初始状态,可填入数字记录,并压栈,然后取其中一个数,填入board,并重新计算该slot所在的行、列、小格子的状态,继续遍历;
  3. 遍历时,如果计算的可填入数字为0,则回溯。回溯前需还原该格子涉及的状态;每取一次可填入数字时,将对应的bit位置0,标记该值已经尝试过,下次不再尝试。

Code in C++

class Solution {
    struct State {
        int rowFlag;
        int colFlag;
        int boxFlag;
        int avail;
        
        State(int r, int c, int b, int a):rowFlag(r), colFlag(c), boxFlag(b), avail(a) {}
    };
    struct Slot {
        int x;
        int y;
        
        Slot(int a, int b): x(a), y(b) {}
    };
    
public:
    void solveSudoku(vector<vector<char>>& board) {
        int cursor = 0;
        stack<State> flags;
        
        vector<int> rowFlags(9, 0);
        vector<int> colFlags(9, 0);
        vector<int> boxFlags(9, 0);
        vector<Slot> slots;
        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                if (board[i][j] == '.')
                    slots.push_back(Slot(i, j));
                else {
                    int digit = 1 << (board[i][j] - '1');
                    rowFlags[i] |= digit;
                    colFlags[j] |= digit;
                    boxFlags[(i/3) + 3*(j/3)] |= digit;
                }
            }
        }
        
        int i = 0, slotsSize = slots.size();
        int x = slots[i].x, y = slots[i].y;
        int avail = 0x1FF ^ (rowFlags[x] | colFlags[y] | boxFlags[(x/3) + 3*(y/3)]);
        flags.push(State(rowFlags[x], colFlags[y], boxFlags[(x/3) + 3*(y/3)], avail));
        while (!flags.empty()) {
            int bIdx = slots[i].x/3 + 3*(slots[i].y/3);
            // 开始尝试该位置的可用值;
            State &s = flags.top();
            if(s.avail != 0) {  
                int bit = GetAndSetAvail(board, slots[i], s.avail);                         
                rowFlags[slots[i].x] = s.rowFlag | bit;
                colFlags[slots[i].y] = s.colFlag | bit;
                boxFlags[bIdx] = s.boxFlag | bit;
                
                ++i;
                if (i == slots.size())
                    break;
                
                bIdx = slots[i].x/3 + 3*(slots[i].y/3);
                avail = 0x1FF ^ (rowFlags[slots[i].x] | colFlags[slots[i].y] | boxFlags[bIdx]);

                flags.push(State(rowFlags[slots[i].x], colFlags[slots[i].y], boxFlags[bIdx], avail));
            }
            else {  // 回溯
                rowFlags[slots[i].x] = s.rowFlag;
                colFlags[slots[i].y] = s.colFlag;
                boxFlags[bIdx] = s.boxFlag;
                flags.pop();
                --i;
            }
        }
        
        return;
    }
    
    int GetAndSetAvail(vector<vector<char>> &board, Slot &s, int &avail) {
        int bit = 1, digit = 1;
        while (!(avail & bit)) {
            ++digit;
            bit <<= 1;
        } 
        avail ^= bit;
        
        board[s.x][s.y] = '0' + digit;
        
        return bit;
    }
};

性能

在这里插入图片描述

优化方案


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值