37. 解数独

这一题是回溯的思路,在做到网易的笔试的时候是这一题的进阶,先说一下这一题的两种思路

回溯

回溯的基本思路:

  1. 三个数组,分别存储行列宫中目前已有的元素
  2. 遍历当前数组,将已有的元素填入进去,三个数组初始化为存在该值为true
  3. 再次遍历数组,回溯的进行插入
    1. 对于每一个位置,有9种可选数据,我们的选择就是,试插
    2. 如果在插入某个数据破坏了不重复性的时候,回溯到3.a,再次试插
    3. 插入满了所有元素,那么就返回答案

填空类 - 图1
代码如下,使用到了三个小技巧:

  1. 宫的编码方式:let p_id = i/3 * 3 + j / 3;
  2. 位置的编码方式,或者说一个数字存储二维数组坐标: let i = n / 9;

let j = n % 9;

  1. 获取到数字的ASCII码:(0 as u8 + 0x30) as char

    1. /// 37. 解数独:一个9阶空缺数量高的树
    2. pub fn solve_sudoku(board: &mut Vec<Vec<char>>) {
    3. // 三个数组存储信息
    4. let mut line_used = vec![vec![false; 9]; 9];
    5. let mut row_used = vec![vec![false; 9]; 9];
    6. let mut palace_used = vec![vec![false; 9]; 9];
    7. for i in 0..board.len() {
    8. for j in 0..board[0].len() {
    9. if board[i][j] == '.' {
    10. continue;
    11. }else {
    12. // 注意我们实际存储的是0-8,对应1-9使用过的数
    13. let num = (board[i][j] as usize) - ('1' as usize);
    14. // 注意这一步对宫的编码:最后算出来就是0-9的宫编号
    15. let p_id = i/3 * 3 + j / 3;
    16. line_used[i][num] = true;
    17. row_used[j][num] = true;
    18. palace_used[p_id][num] = true;
    19. }
    20. }
    21. }
    22. Self::dfs_ss(board, &mut line_used, &mut row_used, &mut palace_used, 0);
    23. }
    24. fn dfs_ss(board: &mut Vec<Vec<char>>, line_used: &mut Vec<Vec<bool>>,
    25. row_used: &mut Vec<Vec<bool>>, palace_used: &mut Vec<Vec<bool>>,
    26. n: usize) -> bool
    27. {
    28. if n == 81 {
    29. return true;
    30. }
    31. // 这一步解码坐标信息很关键!!!
    32. let i = n / 9;
    33. let j = n % 9;
    34. if board[i][j] != '.' {
    35. return Self::dfs_ss(board, line_used, row_used, palace_used, n + 1);
    36. }
    37. let p_id = i/3 * 3 + j / 3;
    38. // 高度n问题,找到了当前的空缺点:
    39. for num in 0..9 {
    40. // 高度n问题,num阶
    41. if line_used[i][num] || row_used[j][num] || palace_used[p_id][num] {
    42. continue
    43. }
    44. line_used[i][num] = true;
    45. row_used[j][num] = true;
    46. palace_used[p_id][num] = true;
    47. // 转换的方法
    48. board[i][j] = (num as u8 + 0x31) as char;
    49. // 如果高度n+1问题,九种可能全部失败,则回溯重新找高度n问题
    50. match Self::dfs_ss(board, line_used, row_used, palace_used, n + 1) {
    51. true => { return true; }
    52. false => {
    53. line_used[i][num] = false;
    54. row_used[j][num] = false;
    55. palace_used[p_id][num] = false;
    56. // board[i][j] = '.'; 不用在这里重置,因为此时在高度n问题,只是走到num+1阶
    57. }
    58. }
    59. }
    60. // 当运行到这里的时候,说明高度n问题9个阶都找过并失败了,因此需
    61. // 要回溯上去,使得n-1问题选择下一个num,需要把此时的节点重新置
    62. // 回.,否则会被高度n问题的判断给清除掉,这个点忽视了
    63. board[i][j] = '.';
    64. false
    65. }