LeetCode第37题 解数独

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

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

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

LeetCode第37题 解数独_第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"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:

LeetCode第37题 解数独_第2张图片

提示:

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

python实现

class Solution(object):
        
    def solveSudoku(self, board):
           
        """
        :type board: List[List[str]]
        :rtype: None Do not return anything, modify board in-place instead.
        """
        # rowVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
        # colVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
        # blockVisit, key 是每个宫格的位置,value 是一个数组标识每个数字是否出现过
        # hasTry, key 是每个宫格的位置,value 是一个哈希表,标识某个数字是否尝试过
        rowVisit = {}
        colVisit = {}
        blockVisit = {}
        hasTry = {}
        # visit 表示第i行第j列是否被填过
        visit = [[0 for _ in range(9)] for _ in range(9)]
        n  = 9
        # 遍历每个初始的位置
        # 将rowVisit,rowVisit,blockVisit三张表填写完整,并填写hasTry和visit
        for i in range(9):
            for j in range(9):
                if i not in rowVisit:
                    rowVisit[i] = [0 for _ in range(10)]
                if j not in colVisit:
                    colVisit[j] = [0 for _ in range(10)]
                blockid = (i // 3, j // 3)
                if blockid not in blockVisit:
                    blockVisit[blockid] = [0 for _ in range(10)]
                hasTry[(i//3, j//3)] = set()
        for i in range(9):
            for j in range(9):
                if board[i][j] != '.':
                    rowVisit[i][ord(board[i][j])-ord('0')] = 1
                    colVisit[j][ord(board[i][j])-ord('0')] = 1
                    blockid = (i//3, j// 3)
                    blockVisit[blockid][ord(board[i][j])-ord('0')] = 1
                    visit[i][j] = 1
        # 存结果,是个列表,为了防止可能有多个答案
        result = []
        self.process(board, 0, 0, rowVisit, colVisit, blockVisit, visit, hasTry, result)
        # 将最终结果的第一个答案填回到board中
        for i in range(9):
            board[i] = result[0][i][:]    
            
    def process(self, board, x, y, rowVisit, colVisit, blockVisit, visit, hasTry, result):
        # board 原来的题目的内存地址
        # rowVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
        # colVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
        # blockVisit, key 是每个宫格的位置,value 是一个数组标识每个数字是否出现过
        # visit 表示第i行第j列是否被填过
        # hasTry, key 是每个宫格的位置,value 是一个哈希表,标识某个数字是否尝试过
        # result存结果,是个列表,为了防止可能有多个答案

        # 来到第10行,说明前面的格子都已经完成了
        # 将结果保持在result当中,返回true
        if x >= 9:
            res = [row[:] for row in board]
            result.append(res)
            return True
        # 来到第10列,说明这一行的前面的格子都已经填写完了
        if y >= 9:
            # 列数归1, 行数+1,尝试下一列
            return self.process(board, x+1, 0, rowVisit, colVisit, blockVisit, visit, hasTry, result)
        # 已经填写过这个格子了,尝试下一列
        if visit[x][y] == 1:
            return self.process(board, x, y+1, rowVisit, colVisit, blockVisit, visit, hasTry, result)
        else:
            # 定位是哪个宫格的格子
            blockid = (x//3, y//3)
            flag = False
            # 尝试1~9 中间的每个数字
            for i in range(1, 10):
                # 如果有数字和当前尝试同一行、同一列、或者同一宫格。尝试下一个数字
                if rowVisit[x][i] == 1 or colVisit[y][i] == 1 or blockVisit[blockid][i] == 1:
                    continue
                # 当前行、当前列、当前宫格的数字填上
                rowVisit[x][i] = 1
                colVisit[y][i] = 1
                blockVisit[blockid][i] = 1
                # board中填好需要尝试的数字
                board[x][y] = chr(ord('0') + i)
                # 当前位置已经填写完毕
                visit[x][y] = 1
                flag |= self.process(board, x, y+1, rowVisit, colVisit, blockVisit, visit, hasTry, result)
                # 回溯返回没有填过的状态
                rowVisit[x][i] = 0
                colVisit[y][i] = 0
                blockVisit[blockid][i] = 0
                board[x][y] = '.'
                visit[x][y] = 0
            return flag

Java实现

class Solution {
    // Point 类,存放x,y的矩阵点信息
    public static class Point{
        int x;
        int y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public String toString() {
            return "Point{" +
                    "x=" + x +
                    ", y=" + y +
                    '}';
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Point point = (Point) o;
            return x == point.x && y == point.y;
        }

        @Override
        public int hashCode() {
            return Objects.hash(x, y);
        }
    }

    public void solveSudoku(char[][] board) {
        // rowVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
        // colVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
        // blockVisit, key 是每个宫格的位置,value 是一个数组标识每个数字是否出现过
        // hasTry, key 是每个宫格的位置,value 是一个哈希表,标识某个数字是否尝试过
        HashMap rowVisit = new HashMap<>();
        HashMap colVisit = new HashMap<>();
        HashMap blockVisit = new HashMap<>();
        HashMap> hasTry = new HashMap<>();
        // visit 表示第i行第j列是否被填过
        int[][] visit = new int[9][9];
        int n = 9;
        // 遍历每个初始的位置
        // 将rowVisit,rowVisit,blockVisit三张表填写完整,并填写hasTry和visit
        for(int i=0;i());
            }
        }
        for(int i=0; i='0'){
                    rowVisit.get(i)[board[i][j]-'0'] = 1;
                    colVisit.get(j)[board[i][j]-'0'] = 1;
                    Point blockid = new Point(i/3, j/ 3);
                    blockVisit.get(blockid)[board[i][j]-'0'] = 1;
                    visit[i][j] = 1;
                }
            }
        }
        // 存结果,是个列表,为了防止可能有多个答案
        ArrayList result = new ArrayList<>() ;
        process(board, 0, 0, rowVisit, colVisit, blockVisit, visit, hasTry,result);
        // 将最终结果的第一个答案填回到board中
        for(int i=0; i<9; i++){
            board[i] = Arrays.copyOf(result.get(0)[i], 9);
        }
    }

    public boolean process(char[][] board, int x, int y,
                           HashMap rowVisit,
                           HashMap colVisit,
                           HashMap blockVisit,
                           int[][] visit,
                           HashMap> hasTry, ArrayList result){
        // board 原来的题目的内存地址
        // rowVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
        // colVisit, key 表示第key行,value 是一个数组表示每个数字是否出现过
        // blockVisit, key 是每个宫格的位置,value 是一个数组标识每个数字是否出现过
        // visit 表示第i行第j列是否被填过
        // hasTry, key 是每个宫格的位置,value 是一个哈希表,标识某个数字是否尝试过
        // result存结果,是个列表,为了防止可能有多个答案

        // 来到第10行,说明前面的格子都已经完成了
        // 将结果保持在result当中,返回true
        if(x>=9){
            char[][] res = new char[9][9];
            for(int i=0; i<9; i++){
                res[i] = Arrays.copyOf(board[i], 9);
            }
            result.add(res);
            return true;
        }
        
        // 来到第10列,说明这一行的前面的格子都已经填写完了
        if(y>=9){
            // 列数归1, 行数+1,尝试下一列
            return process(board, x+1, 0, rowVisit, colVisit, blockVisit, visit, hasTry, result);
        }
        // 已经填写过这个格子了,尝试下一列
        if(visit[x][y]==1){
            return process(board, x, y+1, rowVisit, colVisit, blockVisit, visit, hasTry, result);
        }else{
            // 定位是哪个宫格的格子
            Point blockid = new Point(x/3, y/ 3);
            boolean flag = false;
            // 尝试1~9 中间的每个数字
            for(int i=1; i<=9 ; i++){
                // 如果有数字和当前尝试同一行、同一列、或者同一宫格。尝试下一个数字
                if(rowVisit.get(x)[i]==1||colVisit.get(y)[i]==1||blockVisit.get(blockid)[i]==1){
                    continue;
                }
                // 当前行、当前列、当前宫格的数字填上
                rowVisit.get(x)[i] = 1;
                colVisit.get(y)[i] = 1;
                blockVisit.get(blockid)[i] = 1;
                // board中填好需要尝试的数字
                board[x][y] = (char)('0' + i);
                // 当前位置已经填写完毕
                visit[x][y] = 1;
                // 尝试下一个位置
                flag |= process(board, x, y+1, rowVisit, colVisit, blockVisit, visit, hasTry, result);
                // 回溯返回没有填过的状态
                rowVisit.get(x)[i] = 0;
                colVisit.get(y)[i] = 0;
                blockVisit.get(blockid)[i] = 0;
                board[x][y] = '.';
                visit[x][y] = 0;
            }
            return flag;
        }
    }
}

你可能感兴趣的:(算法)