解数独(java)----完成大学一未解之愿

本文介绍了一种基于深度优先搜索的数独求解算法,通过逐步优化和深度遍历的方法来寻找数独的有效解决方案。

###废话少说,先上程序(直接将代码copy到一个All的java文件中,删除package即可运行,答案将显示到控制台):
Tip:sourceInput为数独的初始状态,可以自行更改


--
package mySuduku;

import java.util.ArrayList;
import java.util.List;
public class All {
   public static void main(String args[]){
       /*
        * 初始数据(map)    
        */
       int sourceInput[][]={    {0,0,7,0,5,0,0,0,0},
                               {6,0,0,2,0,1,7,0,0},
                               {0,1,2,0,0,0,4,0,8},
                               {3,2,4,0,0,0,1,0,0},
                               {0,0,0,0,0,0,0,0,0},
                               {0,0,0,0,6,0,8,0,0},
                               {0,0,0,5,0,0,3,0,0},
                               {7,0,0,8,0,0,0,4,0},
                               {8,4,0,0,0,3,5,7,0}};
       /*    用来分析的map
        * 后期可以填补,以便简化map
        */
       int inputMap[][]=new int[9][9];

       /*
        * 分析使用的map(使map不能再优化)
        * analyInputMap[x][y][z]中xy对应inputMap[x][y],[z]中1-9代表1-9的可能性,
        * 10代表这个数是否在原map中已经存在
        */
       int analyInputMap[][][]=new int[9][9][10];

       //与分析的map做比较用的
       int tempAnalyMap[][][]=new int[9][9][10];

       //比较两个map(这个看是否是优化为最终结果)
       boolean compareInputToTemp=true;

       //将sourceInput塞入inputMap中
       setSameMap(sourceInput,inputMap,9,9);

       /*
        * 去除inputMap中有的值
        * 即置analyInputMap[x][y][z]中的z位为该数(map[x][y])不可用
       */
       deleteFromMap(sourceInput,analyInputMap);
       
       /*
        * 两步删除不可能的结果
        * 两步分别指:    1对所有剩下的空白格,求出其可能的数值,
        *                2当其可能数值仅有一种时,更新inputMap填上这一种,然后将analyInputMap进行调整
        * 当输入和输出的值一样的时候认为无法再进一步简化
        */    
       while(compareInputToTemp){
           //赋值inputMap到tempMap
           setSameMap(analyInputMap,tempAnalyMap,9,9,10);
           //检查是否可以进一步简化
           arrange(inputMap,analyInputMap);
           //判断上步简化是否确实发生了变化
           compareInputToTemp=compareAB(analyInputMap,tempAnalyMap,9,9,10);
           printMap(inputMap,9,9);
           printMap(analyInputMap,9,9,9);
       }
       
       /*
        *         while后结果可视为初步最优解,此时考虑遍历所有剩下的可能情况,用countRest记录剩余空白格
        *         对每个空白格需记录这么几个项:1坐标(即x,y),2可能的数字
        *         这里采用一个类(theRest)记录
        *         类中的X,Y记录坐标,avaiable记录可能的数值
        *         
        *         最后,用List<theRest> theRestNumber装下所有theRest
       */
       
       //剩下还有多少空没有填
       int countRest=0;
       //每个填的数字需要用个数据结构(类theRest)
       List<theRest> theRestNumber=new ArrayList<theRest>();
       //一个暂时的temp指针,指向一个theRest类,这里先设置了,减小开销
       theRest temp;
       for(int i=0;i<9;i++)
           for(int j=0;j<9;j++)
               if(inputMap[i][j]==0){
                   countRest++;                    
                   temp=new theRest(i,j);
                   for(int k=0;k<9;k++){
                       if(analyInputMap[i][j][k]==0)
                       temp.avaiable.add(k+1);
                   }
                   theRestNumber.add(temp);
               }
       //试试一个数字
       tryANumber(inputMap,countRest,countRest,theRestNumber);
   }
   
   /*    
    * 试试一个number
    * 整体思路如下,输入参数为map(最终优化后结果),计数,整体theRestNumber的length,theRestNumber
    * 出口就是填满所有数字(由于是先检查,再填数字,所以最后不用检查)
   */
   public static void tryANumber(int[][] map,int nowRest,int allRestNum,List<theRest> theRestNumber){
/*        printMap(map,9,9);*/
       if(nowRest==0){
           printMap(map,9,9);
       }
       else if(allRestNum-nowRest>=0&&allRestNum!=0){
           theRest temp=theRestNumber.get(allRestNum-nowRest);
           boolean flag=false;
           int i;
           for(i=0;i<temp.avaiable.size();i++){
               flag=check(map,temp,temp.avaiable.get(i));
               //如果值是可用的,则将值放入,并进行下一次的迭代
               if(flag)
               {
                   map[temp.x][temp.y]=temp.avaiable.get(i);
                   tryANumber(map,nowRest-1,allRestNum,theRestNumber);
                   //当try遍历完成后,将尝试的值置空
                   map[temp.x][temp.y]=0;
               }
           }
       }            
   }
   //检查放入一个number以后是否可行
   public static boolean check(int[][] map,theRest temp,int tryNumber){
       boolean flag=true;
       //检查横行和竖行是否有数字已经要尝试的数字了
       for(int i=0;i<9;i++)
           if(map[temp.x][i]==tryNumber||map[i][temp.y]==tryNumber)
               flag=false;
       //排除坐标对应3*3格中有没有尝试的数字
       int tempx=temp.x/3;
       int tempy=temp.y/3;
       for (int i=0;i<3;i++)
           for(int j=0;j<3;j++)
               if(map[tempx*3+i][tempy*3+j]==tryNumber)
                   flag=false;
       return flag;
   }
   //从分析的map中删除map中有的元素(即置标签位(z=9)为空)
   public static void     deleteFromMap(int[][] source,int[][][] target){
       for(int i=0;i<9;i++)
           for(int j=0;j<9;j++)
               if(source[i][j]!=0)
                   target[i][j][9]=1;                    
   };
   /*
    * 安置函数,入参是inputMap,和analyInputMap
    * 当a(即inputMap)每出现一个数字时,在对应的b(即analyInputMap)中进行相应动作(controlB)
    * 该动作排除a出现后限制b中的可能性(即a中空白处对应的可能数字要相应减少)
    * 在动作后,对b经行遍历,发现若a的空白处可填项仅有一个时,对a进行填项,填项后对b的该坐标位置不可填
    */
   public static void arrange(int[][] a,int[][][]b){
       for(int i=0;i<9;i++)
           for(int j=0;j<9;j++)
               if(a[i][j]!=0)
                   controlB(i,j,a[i][j],b);
       //是否a的空白出只有一个可填项
       int countIsOnly;
       //对b经行遍历,发现若a的空白处可填项仅有一个时,对a进行填项,填项后对b的该坐标位置不可填
       for(int i=0;i<9;i++)
           for(int j=0;j<9;j++)
               if(a[i][j]==0&&b[i][j][9]!=1){
                   countIsOnly=9;
                   //循环,发现1则减去,当8个1时,说明只有一个可填,填项
                   for(int k=0;k<9;k++)
                       countIsOnly-=b[i][j][k];
                   if(countIsOnly==1){
                       for(int k=0;k<9;k++)
                           if(b[i][j][k]==0)
                               a[i][j]=k+1;
                       b[i][j][9]=1;
                   }
                           
               }
   }
   
   public static void controlB(int x,int y,int value,int[][][] b){
       for(int i=0;i<9;i++){
           //排除每行第x个字
           b[x][i][value-1]=1;
           //排除每列第x个字
           b[i][y][value-1]=1;
       }
       //排除坐标对应的3*3格
       int tempx=x/3;
       int tempy=y/3;
       for (int i=0;i<3;i++)
           for(int j=0;j<3;j++){
               b[tempx*3+i][tempy*3+j][value-1]=1;
           }
   }
   //复制二维数组函数,不多说
   public static void setSameMap(int[][]source,int[][]target,int x,int y){
       for(int i=0;i<x;i++)
           for(int j=0;j<y;j++)
               target[i][j]=source[i][j];        
   }
   //复制三位数组函数,不多说
   public static void setSameMap(int[][][]source,int[][][]target,int x,int y,int z){
       for(int i=0;i<x;i++)
           for(int j=0;j<y;j++)
               for(int k=0;k<z;k++)
                   target[i][j][k]=source[i][j][k];        
   }
   //比较二维数组函数,不多说
   public static boolean compareAB(int[][]a,int[][] b,int x,int y){
       boolean flag=true;
       for(int i=0;i<x;i++)
           for(int j=0;j<y;j++)
               if(b[i][j]!=a[i][j])
                   flag=false;
       return flag;
   }
   //比较三维数组函数,不多说
   public static boolean compareAB(int[][][]a,int[][][] b,int x,int y,int z){
       boolean flag=false;
       for(int i=0;i<x;i++)
           for(int j=0;j<y;j++)
               for(int k=0;k<z;k++)
                   if(b[i][j][k]!=a[i][j][k])
                       flag=true;
       return flag;
   }
   //打印二维map,不多说
   public static void printMap(int[][] map,int x,int y){
       for(int i=0;i<x;i++){
           System.out.println();
           for(int j=0;j<y;j++){
               System.out.print(map[i][j]+" ");
           }
       }    
       System.out.println();
       
   }
   //打印三维map,不多说
   public static void printMap(int[][][] map,int x,int y,int z){
       for(int i=0;i<x;i++){
           System.out.println();
           for(int j=0;j<y;j++){
               if(map[i][j][9]!=1){
                   System.out.print("map【"+i+"】:"+"【"+j+"】:");
                   for(int k=0;k<9;k++){
                       if(map[i][j][k]!=1)
                           System.out.print(k+1);
                           System.out.print(" ");
                   }
                   
               }
           }
       }    
       System.out.println();
       
   }

}
   /*
    * 一个简单类,用装剩余空白格的信息
    * x,y记录空白处的坐标
    * avaiable记录可填充的数字
   */
class theRest{
   public int x;
   public int y;
   public List<Integer> avaiable=new ArrayList<Integer>();
   public int getX() {
       return x;
   }
   public void setX(int x) {
       this.x = x;
   }
   public int getY() {
       return y;
   }
   public void setY(int y) {
       this.y = y;
   }
   public theRest(int x, int y) {
       super();
       this.x = x;
       this.y = y;
   }
   
}






##程序总体思路:
##1输入状态
##2解决
  
###2.1    将总体状态放入一个sourceInput[9][9]
###2.2    优化sourceInput,用inputMap[9][9]存放最后优化结果
####2.2.1 优化,以下步骤


-  当map中每出现一个数,在其余的横竖和对应的3*3格子内不允许出现该数字,注意剩下的空白格子中可填的数字,
-  当空白格子中可填数字仅剩一个时,填入该数字,
-  重复上两步,直到map不再发生变动
###2.3    对于优化的结果,可记num个空格,每个空格的可能填的数字可能个数记为an,则此时全遍历的情况为a1*a2*a3*…*an(n=num),


###2.4    这里采用思想是深度遍历,当遇到结果就打印,然后继续下一步,直到遍历完(以防止多解情况)


##3输出答案
###Ps:这里仅是一次尝试,还有好多不妥之处,还望见谅~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值