跳至主要內容

37, 解数独

Mike大约 4 分钟backtrackinghardarrayhash tablematrixbacktracking

一、题目描述

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

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

  1. 数字1-9在每一行只能出现一次。
  2. 数字1-9在每一列只能出现一次。
  3. 数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用'.'表示。

示例 1

输入: 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"]]

解释: 输入的数独如上图所示,唯一有效的解决方案如下所示:

提示

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

相关主题

  • 数组
  • 哈希表
  • 回溯
  • 矩阵

二、题解

方法 1: 回溯

pub fn solve_sudoku(board: &mut Vec<Vec<char>>) {
    let mut row = vec![vec![false; 9]; 9];
    let mut col = vec![vec![false; 9]; 9];
    let mut sub_boxes = vec![vec![vec![false; 3]; 3]; 9];
    let mut spaces = vec![];
    let mut valid = false;

    for i in 0..9 {
        for j in 0..9 {
            if board[i][j] == '.' {
                spaces.push((i, j));
            } else {
                let idx = (board[i][j] as u8 - b'1') as usize;
                (row[i][idx], col[j][idx], sub_boxes[idx][i / 3][j / 3]) = (true, true, true);
            }
        }
    }

    const DFS: fn(
        &mut Vec<Vec<char>>,
        usize,
        &Vec<(usize, usize)>,
        &mut Vec<Vec<bool>>,
        &mut Vec<Vec<bool>>,
        &mut Vec<Vec<Vec<bool>>>,
        &mut bool,
    ) = |board, pos, spaces, row, col, sub_boxes, valid| {
        if pos == spaces.len() {
            *valid = true;
            return;
        }

        let (i, j) = spaces[pos];
        for idx in 0..9 {
            if *valid {
                break;
            }
            if !row[i][idx] && !col[j][idx] && !sub_boxes[idx][i / 3][j / 3] {
                (row[i][idx], col[j][idx], sub_boxes[idx][i / 3][j / 3]) = (true, true, true);
                board[i][j] = (b'1' + (idx as u8)) as char;

                DFS(board, pos + 1, spaces, row, col, sub_boxes, valid);

                (row[i][idx], col[j][idx], sub_boxes[idx][i / 3][j / 3]) = (false, false, false);
            }
        }
    };

    DFS(board, 0, &spaces, &mut row, &mut col, &mut sub_boxes, &mut valid);
}

方法 2: 位运算优化

pub fn solve_sudoku(board: &mut Vec<Vec<char>>) {
    let mut row = vec![0; 9];
    let mut col = vec![0; 9];
    let mut sub_boxes = vec![vec![0; 3]; 3];
    let mut spaces = vec![];
    let mut valid = false;

    for i in 0..9 {
        for j in 0..9 {
            if board[i][j] == '.' {
                spaces.push((i, j));
            } else {
                let digit = (board[i][j] as u8 - b'1') as usize;
                row[i] ^= 1 << digit;
                col[j] ^= 1 << digit;
                sub_boxes[i / 3][j / 3] ^= 1 << digit;
            }
        }
    }

    const DFS: fn(
        &mut Vec<Vec<char>>,
        usize,
        &Vec<(usize, usize)>,
        &mut Vec<i32>,
        &mut Vec<i32>,
        &mut Vec<Vec<i32>>,
        &mut bool,
    ) = |board, pos, spaces, row, col, sub_boxes, valid| {
        if pos == spaces.len() {
            *valid = true;
            return;
        }

        let (i, j) = spaces[pos];
        let mut mask = !(row[i] | col[j] | sub_boxes[i / 3][j / 3]) & 0x1ff;
        while mask != 0 && !*valid {
            let digit = (mask & (-mask)).trailing_zeros();
            row[i] ^= 1 << digit;
            col[j] ^= 1 << digit;
            sub_boxes[i / 3][j / 3] ^= 1 << digit;
            board[i][j] = (digit as u8 + b'1') as char;

            DFS(board, pos + 1, spaces, row, col, sub_boxes, valid);

            row[i] ^= 1 << digit;
            col[j] ^= 1 << digit;
            sub_boxes[i / 3][j / 3] ^= 1 << digit;

            mask &= mask - 1;
        }
    };

    DFS(board, 0, &spaces, &mut row, &mut col, &mut sub_boxes, &mut valid);
}