1 import java.util.Scanner;
2 import java.util.Set;
3 import java.util.TreeSet;
4
5 /*
6 * 解密9*9数独:
7 * 格子内填充1-9的数字(空格用0代替),使任意格子的行,列,九宫格都出现1-9的数字
8 * 注:对于空格较多的数独,遍历层次较深,需要使用更多的栈内存,应适当调节栈内存配置后运行
9 * 例:
10 * 8 0 0 0 0 0 0 0 0
11 * 0 0 3 6 0 0 0 0 0
12 * 0 7 0 0 9 0 2 0 0
13 * 0 5 0 0 0 7 0 0 0
14 * 0 0 0 0 4 5 7 0 0
15 * 0 0 0 1 0 0 0 3 0
16 * 0 0 1 0 0 0 0 6 8
17 * 0 0 8 5 0 0 0 1 0
18 * 0 9 0 0 0 0 4 0 0
19 * 结果:
20 * 8 1 2 7 5 3 6 4 9
21 * 9 4 3 6 8 2 1 7 5
22 * 6 7 5 4 9 1 2 8 3
23 * 1 5 4 2 3 7 8 9 6
24 * 3 6 9 8 4 5 7 2 1
25 * 2 8 7 1 6 9 5 3 4
26 * 5 2 1 9 7 4 3 6 8
27 * 4 3 8 5 2 6 9 1 7
28 * 7 9 6 3 1 8 4 5 2
29 */
30 public class Main {
31
32 static int[][] source = new int[9][9];//原始数组
33
34 static int[][] target = new int[9][9];//结果数组
35
36 public static void main(String[] args) {
37 Scanner scanner = new Scanner(System.in);
38
39 /*
40 * 空格从最上面的行开始,逐行从左向右填充数值
41 */
42 Integer firstX = null;//第一个空格的行号
43 Integer firstY = null;//第一个空格的列号
44
45 /*
46 * 数组录入输入的数字,并获得第一个空格的行号和列号
47 */
48 for(int i=0;i<9;i++){
49 for(int j=0;j<9;j++){
50 int c = scanner.nextInt();
51 source[i][j] = c;
52 target[i][j] = c;
53 if(firstX==null && firstY==null && source[i][j]==0){
54 firstX = i;
55 firstY = j;
56 }
57 }
58 }
59
60 //开始遍历填充
61 go(firstX,firstY);
62
63 //打印结果数组
64 for(int i=0;i<9;i++){
65 for(int j=0;j<9;j++){
66 System.out.print(target[i][j]+" ");
67 }
68 System.out.println("");
69 }
70 }
71
72 public static void go(int x,int y){
73 boolean flag = build(x,y);//填充
74
75 if(flag){//填充成功,获取下一个空格的行号和列号,以源数组为参照
76 do{
77 if(x==8&&y==8){//最后的空格填充完毕,结束填充
78 return;
79 }
80 if(y==8){
81 y=0;
82 x=x+1;
83 }else{
84 y=y+1;
85 }
86 }while(source[x][y]!=0);
87 }else{//填充失败,获取上一个空格的行号和列号,以源数组为参照(因为上一个空格已被填充过,所以需要以源数组作为参照)
88 do{
89 if(x==0&&y==0){//退到第一个空格且无可填充的值,证明无解,结束
90 return;
91 }
92 if(y==0){
93 y=8;
94 x=x-1;
95 }else{
96 y=y-1;
97 }
98 }while(source[x][y]!=0);
99 }
100 /*
101 * 1.如果当前空格填充成功,则填充的是下一个空格
102 * 2.如果当前空格填充失败,则对上一个已填充的空格重新填充
103 */
104 go(x,y);
105 }
106
107 /*
108 * 1.填充时,优先填充1-9中的较小值
109 * 2.填充时,会有两种情况:1.新空格填充,空格值等于0 2.因后续空格无法继续填充,老空格
110 * 尝试填充新的值,再进行后续空格填充
111 * 3.经过填充后,空格没有获得新值,说明已无合适数字进行填充,需要将空格重置成0,返回f-
112 * -alse对上一空格进行重新填充
113 * 4.经过填充后,空格获得新值,返回true根据结果判断对后续空格进行填充
114 */
115 public static boolean build(int x,int y){
116 int r = target[x][y];
117 for(int n=r+1;n<10;n++){
118 target[x][y] = n;
119 if(isExists(x,y)){
120 target[x][y] = r;
121 }else{
122 break;
123 }
124 }
125 if(target[x][y]==r){
126 target[x][y]=0;//已无可填充数字,将格子重置成0,并重新填充上一个格子
127 return false;
128 }else{
129 return true;
130 }
131 }
132
133 //判断当前空格的值是否已存在于同行,同列或同九宫格的其他格子中
134 public static boolean isExists(int x,int y){
135 Set<Integer> set = getExists(x,y);
136 return set.contains(target[x][y]);
137 }
138
139 //获取当前格子的行,列,九宫格中已填充的数字
140 public static Set getExists(int x, int y){
141 Set<Integer> set = new TreeSet<Integer>();
142 for(int i=0;i<9;i++){
143 if(i==x){
144 continue;
145 }
146 if(target[i][y]==0){
147 continue;
148 }
149 set.add(target[i][y]);
150 }
151 for(int i=0;i<9;i++){
152 if(i==y){
153 continue;
154 }
155 if(target[x][i]==0){
156 continue;
157 }
158 set.add(target[x][i]);
159 }
160 int xn = x/3;
161 int yn = y/3;
162 for(int i=xn*3;i<xn*3+3;i++){
163 for(int j=yn*3;j<yn*3+3;j++){
164 if(i==x&&j==y){
165 continue;
166 }
167 if(target[i][j]==0){
168 continue;
169 }
170 set.add(target[i][j]);
171 }
172 }
173 return set;
174 }
175
176 }