回溯法数独求解

2019-08-08 00:00:00 回溯 求解 数独
public class SoduTest {
    private int[][] sodu = {
            // 最难的
//            {0,0,5,3,0,0,0,0,0},
//            {8,0,0,0,0,0,0,2,0},
//            {0,7,0,0,1,0,5,0,0},
//            {4,0,0,0,0,5,3,0,0},
//            {0,1,0,0,7,0,0,0,6},
//            {0,0,3,2,0,0,0,8,0},
//            {0,6,0,5,0,0,0,0,9},
//            {0,0,4,0,0,0,0,3,0},
//            {0,0,0,0,0,9,7,0,0}

            // 包含两个解的
            {7,6,1,2,4,5,8,3,9},
            {3,2,8,1,7,9,4,5,6},
            {0,0,4,8,3,6,7,2,1},
            {8,7,5,9,6,4,2,1,3},
            {2,3,9,7,5,1,6,4,8},
            {1,4,6,2,8,3,9,7,5},
            {4,1,7,3,9,8,5,6,2},
            {0,0,3,4,2,6,1,8,7},
            {6,8,2,5,1,7,3,9,4}
    };
    public static void main(String[] args) {
        long timeStart = System.currentTimeMillis();
        new SoduTest().getSoduAnswer(0, 0);
        long timeStop = System.currentTimeMillis();
        System.out.println("It took " + (timeStop - timeStart) + " milliseconds");
    }

    /**
     * 核心算法,利用递归回溯,求出所有解
     * @param x 起始第 x 行
     * @param y 起始第 y 列
     * @return
     */
    private boolean getSoduAnswer(int x, int y) {
        if(y == 9) {
            // 终止条件
            if(x == 8) {
                // 打印
                printSodu(this.sodu);
                System.out.println("-------------------------------------------");
                // 若返回true,则求出一个解后退出
//                return true;
                // 若返回false,则求出一个解后向前回溯,直到求出所有解
                return false;
            }
            // 换行
            x++;
            y = 0;
        }
        // 如果不为0,直接计算下一个数
        if(this.sodu[x][y] != 0) {
            return getSoduAnswer(x, y+1);
        }
        // 尝试 1-9 是否满足条件
        for(int i = 1; i <= 9; i++) {
            if(this.checkSoduNum(x, y, i)) {
                //满足条件设置值
                this.sodu[x][y] = i;
                //并尝试下一个数是否满足条件
                if(getSoduAnswer(x, y+1)) {
                    return true;
                }else {
                    // 若不满足条件,进行回溯
                    this.sodu[x][y] = 0;
                }
            }
        }
        return false;
    }

    /**
     * 判断值是否合理
     * @param x
     * @param y
     * @param soduNum
     * @return
     */
    private boolean checkSoduNum(int x, int y, int soduNum) {
        // 判断当前行,列是否有重复
        for(int i = 0;i < 9;i++) {
            if(this.sodu[x][i] == soduNum) {
                return false;
            }
            if(this.sodu[i][y] == soduNum) {
                return false;
            }
        }
        // 求出左上角坐标
        int a = x - (x % 3);
        int aStop = a+3;
        int b = y - (y % 3);
        int bStop = b+3;
        // 判断当前方框内是否有重复
        for(a = x - (x % 3);a<aStop;a++) {
            for(b = y - (y % 3);b<bStop;b++) {
                if(this.sodu[a][b] == soduNum) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 打印数组
     * @param sodu
     */
    public static void printSodu(int[][] sodu) {
        for(int i = 0; i < sodu.length; i++) {
            int[] soduX = sodu[i];
            for(int j = 0; j < soduX.length; j++) {
                int soduY = soduX[j];
                if(j != 0 && j % 3 == 0) {
                    System.out.print(" ");
                }
                System.out.print(soduY);
            }
            System.out.println();
            if(i != 0 && (i+1) % 3 == 0) {
                System.out.println();
            }
        }
    }
}

 

相关文章