八皇后问题的实现(思路分析)
我们这里会使用递归的思路来解决八皇后问题
八皇后问题的介绍:
八皇后问题是一个古老而著名的问题,是回溯算法的典型案例,该问题是国际西洋棋棋手马克斯-贝瑟尔于1848年提出的一个问题 : 在8 x 8的国际象棋上摆上八个皇后,使这八个皇后之间不能进行相互攻击(也就是任意两个皇后不能处于同一行,同一列,或者是同一条斜线上,问一共有多少中摆法)
八皇后问题算法思路分析:(回溯算法)
- 第一个皇后先放到第一行第一列
- 第二个皇后放在第二行第一列,然后判断是否ok , 如果不满足条件,则继续放在第二行的第二列 , 第三列 , 依次往下放,知道找到一个合适的位置为止
- 继续放置第三个皇后,还是放到第三行的第一列,第二列,直到第8个皇后也能放到一个不冲突的位置,就算是得到了第一个真确解
- 当得到一个正确解时,我们就要开始回溯,开始退到上一个栈,一直退,边退边后移继续判断(也就是边回溯边递归),直到将第一个皇后放到行的第一列的位置的所有正确解都求出
- 然后让第一个皇后放到第二列,后面就继续重复2 , 3 , 4步骤
难点细节分析:
①其实在放中间的皇后的时候我们就已经开始回溯了,并不是在得到第一个正确解时才可以开始回溯,可能我们开始的时候放好第三个皇后的时候接下来我们假设要放第四个皇后了,此时如果我们永远都摆不好第四个皇后的位置,这个是我们就要进行一个回溯, 我们就要回溯到第三个皇后的位置, 将第三个皇后重新摆放一个位置, 这个位置还是必须要和我们的第一个和第二个皇后是不冲突的,假设这个时候我们只是回溯到了第三个皇后的位置上的时候我们通过继续向后移动第三个皇后的位置使得我们的第三个皇后的位置和我们的第一个皇后的位置和第二个皇后的位置不冲突了,这个时候并且我们重新递归到第四个皇后的位置拜访的时候如果这个时候第四个皇后的位置也可以成功摆放了
②这个时候我们就要开始摆放第五个皇后的位置了,这个时候如果我们的摆放第五个皇后的时候位置又摆放不好了,这个时候我们就要回溯到第四个皇后的位置重新向后遍历我们的第四个皇后的位置,如果后面的位置不能满足第四个皇后的摆放或者是满足了第四个皇后的摆放条件之后这个时候我们第五个皇后还是摆放不好,这个时候我们最终就只能是回溯到我们的第三个皇后的位置了,这个时候我们就要继续将第三个皇后的位置向后移动,如果这个时候我们的第三个皇后也没有位置了这个时候我们就要回溯道我们的第二个皇后的位置,我们就要去向后移动我们的第二个皇后的位置,假如这个时候我们的第二个皇后向后移动之后重新摆放好了,这个时候又会递归到递归到第三个皇后的位置拜访中去,这个时候我们就要找到我们的第三个皇后的排放位置,然后又递归到第四个皇后的位置中去,我们每次回溯的时候都是回溯到了某个位置然后继续开始执行,但是每次我们递归的时候都是从一个初始状态开始执行, 然后假设我们直到第8个皇后的位置摆放好了之后我们就开始继续回溯,直到得到我们的第一个皇后在第一行的第一列位置上的所有的正确解之后就停止此次回溯,然后就退出了最外层的递归,这个时候我们就要开始执行下一次的循环了,也就是要将我们的第一个皇后放到我们的第一行的第二列的位置
- 注意: (我们这里举一个例子: ) 这个时候假设我们回溯到第三皇后时,我们会从我们上一次第二个皇后递归的位置开始向后移动我们的第三个皇后的位置直到我们重新摆放好第三个皇后的位置之后会重新递归到第四个皇后的位置,这个时候我们递归到第四个皇后的位置是重新开始执行的,也就是第四个皇后要从我们的第四行的第一列开始重新向后移动
递归与回溯的思路理清:
假如有一个方法A,我们假设这方法使用了递归调用(调用的就是自己),那么此时我们将第一层栈(也就是第一层A方法)记为A1 , 将A1中递归调用的自己又称之为A2, 将A2中调用的自己又称之为A3,那么我们如果是从A1递归到A2,此时A2就是新开辟的一个方法(对应了一个新开辟的栈空间) , 此时我们就将A2称之为从初始状态开始执行, 假设A2执行到了某个中间状态 m1 的时候进行了一个递归递归到了A3方法中 ,然后A3方法也是从开始状态开始执行,假如A3方法执行到了某个中间状态的时候进行了一个回溯,那么此时一旦回溯之后,A3方法对应的栈空间已经是被处理了(回收了) , 我们此时是回溯到A2方法中的中间状态m2之后的紧接着的一个状态, 那么我们就继续执行A2,如果这个时候我们的A2又重新进行一个递归调用,这个时候还是会重新创建一个栈空间,并重新加载一个A3方法(之前的A3方法已经被处理了)
结论①: 当方法递归时,调用递归方法的上一层级的方法并没有结束,栈空间依然是保留的,而当我们的方法回溯的时候, 执行回溯的方法会立刻借宿, 对应的栈空间也会立刻被回收
结论②: 方法递归时,产生的新的递归方法是从初始状态开始执行的, 而方法回溯时,回溯到的方法是从某个中间状态开始执行的
- 此中间状态就是回溯到的方法上次递归调用的状态的后一个状态的位置
补充:
其实回溯算法就是递归加回溯 , 我们只有有了递归之后才能有回溯,没有递归哪里来的回溯
补充二:
理论上我们应该使用一个二维数组表示棋盘, 但是实际上我们可以通过算法从而实现使用一个一维数组就可以解决这个问题: