最近在上java课遇到了一个八皇后的问题,在博客上搜索八皇后问题发现java版的代码很少,同时解释的也很少,这里将详细解释思路与代码,核心思路为迭代,本文章是给新手看的,大佬可以直接跳过,新手看不懂的话算我输。
首先是问题描述。
思路:
我们的思路是程序要包含三个方法:1.主方法 2.判断方法1 3.判断方法2 4.输出方法
首先说判断方法2,判断方法2的作用是判断该位置的皇后是否合法。本文采用以行的方法,一行一行赋值。所以该方法将当前的列位置与之前存在的皇后的位置进行比较判断并返回true or false。
判断条件有三个:
1.不与之前的皇后同列:从第一个皇后到当前皇后的前一个皇后,循环判断皇后的位置是否与当前的列位置相同,相同返回false。
//j为当前判断的列
//判断是否与之前的那些皇后在一个列
//一直寻找到第k-1个皇后,即上一个皇后
for(int i=0;i
2.不在之前的皇后的左下斜线上:首先要从当前皇后的前一个皇后判断,如果该位置在前一个皇后的左斜线上
则该列位置+1等于前一个皇后的位置(列);以此类推,之后判断前皇后的前一个皇后,记为前前皇后,如果在前前一个皇后的左斜线上,即该列位置+2等于前前一个皇后的位置(列)。
//判断当前列是否在上一个皇后的右下斜线上;
// k为当前寻找的皇后,故上一个皇后为k-1
//xo
//ox
//此时当前列向前一列就会和上一个八皇后的列位置相同
//xoo
//ooo
//oox
//此时需向前两列去比较上上一个八皇后
for (int lie=j-1,hang=k-1;lie>=0&&hang>=0;lie--,hang-- )
{
if(index[hang]==lie) {
return false;
}
}
3.不在之前的皇后的右下斜线上:与之前的同理,首先要从当前皇后的前一个皇后判断,如果该位置在前一个皇后的右斜线上。则该列位置-1等于前一个皇后的位置(列);以此类推,之后判断前皇后的前一个皇后,记为前前皇后,如果在前前一个皇后的左斜线上,即该列位置-2等于前前一个皇后的位置(列)。
//判断当前列是否在上一个皇后的右下斜线上;
//ox
//xo
//当前列向后一列,就会与上一个皇后相的列数相等
for (int lie=j+1,hang=k-1;lie>=0&&hang>=0;lie++,hang-- )
{
if(index[hang]==lie)
{
return false;
}
}
其次说判断方法1,该方法的目的是从第j列到最后一列进行查找,将每一列传入判断放法2,如果返回结果为true则将该列返回。该方法值得一提的是j列并非所有的都是从头开始去查找,因为该问题涉及回溯,比如可能会查找到的皇后位置有误,即没有一列满足要求,此时需要回溯上一个皇后的位置。所以这里的j需要分情况讨论。该方法需要传入当前正在查找的皇后,然后判断该皇后的位置是-1还是有具体位置,如果有具体位置则代表是回溯到这个皇后的,说明存的位置有误,需要从这个位置之后的位置继续查找新的位置,如果为-1,则说明为未查找过的,所以从头开始查找即可。代码如下:index为存放各个皇后位置的数组,index的下表代表行,数值代表列,比如index【0】=1,代表第一个皇后的位置为第一行第二列。
public static int startlie(int k,int index[]){
//体现了回溯的想法,如果这时传来的第k行的皇后的列数为-1
//代表该皇后不存在,则从头开始判断;
//如果第k行的皇后有列数,说明之前传入的不正确,
//所以从该列数向后一列进行寻找。
int lie;
if(index[k]==-1)
{
lie=0;
}
else
lie=index[k]+1;
for(int j=lie;j<8;j++)
{
if(isvaild(k,j,index))
{
return j;
}
}
return -1;
}
最后说下主函数:主函数首先需要将之前代表皇后位置的index数组赋初值-1,并初始化第一个数为0,我们默认将第一个皇后放在左上角。之后要设置一个代表当前寻找的皇后的变量k。之后使用while函数,持续判断k是否小于等于7,初始化变量j代表列数,将第一个判断函数的值赋给j,判断j是否为正数,如果为正数,将j的值赋值index【k】(当前寻找的皇后的位置),k增一,如果为负数说明,遍历了所有列都没有找到合适的列数,代表之前的皇后位置有误,将index【k】==-1,k减1。
public static void main(String[]args) {
//摆放每行的皇后放在哪列
//index[0]=0,代表第一个皇后放在第0列。
int []index= new int[8];
//初始化每个皇后是否存在,如果列数为-1,代表不存在
for(int i=0;i<8;i++)
{
index[i]=-1;
}
//第一个设置在第一列
index[0]=0;
//k为现在寻找的皇后
int k=1;
//找完8个皇后
while(k<=7){
int j;
j=startlie(k,index);
//理论大于0就可以,因为第一个皇后位置为0列,所以其他皇后不能再0列
if(j>=0){
index[k]=j;
k++;
}
else
{
//要将当前不合适的皇后的位置置1,
// 理由:比如当前正在寻找k=5,即第六个皇后,第六个皇后没找到正确位置,回溯第五个皇后
//此时可能觉得不用将第六个皇后的位置置为-1;因为正在找第六个皇后,所以第六个皇后的位置还是初始化的-1,
//但假如回溯第五个皇后也没找到正确位置,而第五个皇后存着的位置为原先位置,
// 此时第五个皇后的位置就要变为-1了,然代表没找到第五个皇后,所以需要将没找到的皇后的位置置为-1.
index[k]=-1;
k--;
//
}
}
for(int i=0;i<8;i++)
{
index[i]=index[i]*2+1;
//System.out.print(index[i]);
}
pp(index);
}
这里初学者会对一个地方有问题也就是else之后为什么要把index【k】赋值为-1呢,看着意思是代表将k的皇后变为没有找过的皇后。但是有人会说,那比如我一直都是正确的皇后位置,我们假设找了四(k=4)次都是对的,当我找第五次(k=5)的时候,我第五次没找到皇后位置,为什么还要将其赋值-1,我不是在最初的数组index里赋值为-1了么,而且我也没有改变其数值阿。这是初学者很容易犯的问题,答案是:比如我找第五次(k=5),也就是第六个皇后位置错了,我要回溯第五个皇后,此时index【5】可以不改,因为之前就是-1,但假如我回溯第五个皇后,又没有找到对的列数,说明第五个位置也错了,此时又到了else里,如果我只k--,的确这里是回溯到第四个皇后了,但是我第五个皇后的位置没有改还是之前错误的位置,这样当我寻找完第四个皇后时,去寻找第五个皇后,第五个皇后还是从错误的位置开始找,就又会找不到,又回去重新找第四个皇后,就陷入了死循环。所以我们要赋值为-1。
最后的输出函数可以忽略,也可以看看,因为题中给出要求要按照题意输出,这个比较简单就不仔细说了。
附上全部代码:
package com.company.chapter7;
// 从行来遍历,
// 三个函数:一个判断列的开始
// 另一个判断列是否满足条件
// 第三个输出
// 全文中i为行,j代表列
public class exercise7q22 {
public static void main(String[]args) {
//摆放每行的皇后放在哪列
//index[0]=0,代表第一个皇后放在第0列。
int []index= new int[8];
//初始化每个皇后是否存在,如果列数为-1,代表不存在
for(int i=0;i<8;i++)
{
index[i]=-1;
}
//第一个设置在第一列
index[0]=0;
//k为现在寻找的皇后
int k=1;
//找完8个皇后
while(k<=7){
int j;
j=startlie(k,index);
//理论大于0就可以,因为第一个皇后位置为0列,所以其他皇后不能再0列
if(j>=0){
index[k]=j;
k++;
}
else
{
//要将当前不合适的皇后的位置置1,
// 理由:比如当前正在寻找k=5,即第六个皇后,第六个皇后没找到正确位置,回溯第五个皇后
//此时可能觉得不用将第六个皇后的位置置为-1;因为正在找第六个皇后,所以第六个皇后的位置还是初始化的-1,
//但假如回溯第五个皇后也没找到正确位置,而第五个皇后存着的位置为原先位置,
// 此时第五个皇后的位置就要变为-1了,然代表没找到第五个皇后,所以需要将没找到的皇后的位置置为-1.
index[k]=-1;
k--;
//
}
}
for(int i=0;i<8;i++)
{
index[i]=index[i]*2+1;
//System.out.print(index[i]);
}
pp(index);
}
public static int startlie(int k,int index[]){
//体现了回溯的想法,如果这时传来的第k行的皇后的列数为-1
//代表该皇后不存在,则从头开始判断;
//如果第k行的皇后有列数,说明之前传入的不正确,
//所以从该列数向后一列进行寻找。
int lie;
if(index[k]==-1)
{
lie=0;
}
else
lie=index[k]+1;
for(int j=lie;j<8;j++)
{
if(isvaild(k,j,index))
{
return j;
}
}
return -1;
}
public static boolean isvaild(int k,int j,int index[])
{
//j为当前判断的列
//判断是否与之前的那些皇后在一个列
//一直寻找到第k-1个皇后,即上一个皇后
for(int i=0;i=0&&hang>=0;lie--,hang-- )
{
if(index[hang]==lie) {
return false;
}
}
//判断当前列是否在上一个皇后的右下斜线上;
//ox
//xo
//当前列向后一列,就会与上一个皇后相的列数相等
for (int lie=j+1,hang=k-1;lie>=0&&hang>=0;lie++,hang-- )
{
if(index[hang]==lie)
{
return false;
}
}
return true;
}
public static void pp(int []index)
{
//0,4,7,5,2,6,1,3
//int[] index ={1,9,15,11,5,13,3,7};
//int n;
for(int i=0;i<8;i++) {
for (int j = 0; j <= 16; j++) {
if (j % 2 != 0) {
if (j == index[i]) {
System.out.print("Q");
} else
System.out.print(" ");
}
else if(j%2==0){
System.out.print("|");
}
}
System.out.println("");
}
}
}
相信你看到这里肯定懂了,那么如果要变成遍历所有的八皇后情况需要怎么做呢,你可以自己试试检验是否真的看懂了