目录
1.八皇后问题
2.N皇后之字符串
3.全排列
这几个题的解法思路一样,可以总结出几个点。
共同修改共享数据,负责递归的函数通常有几个相似的步骤
1.出递归的条件
2.判断是否能进入递归
3.做选择,打比方,八皇后中当前层皇后放在哪个位置。
4.进入下一层。
5.回撤,即发现当前行不通时,将该层的修改改回原来的状态。
完全暴力算法八皇后应该要执行8的8次方,在递归时做合法性判断能缩小不必要的执行次数
回溯发生在「递归调用返回后」,且仅当当前选择的路径走不通(无合法后续)或已经探索完该路径的所有可能性时。用来判断其他的情况。
一.八皇后问题

public class Queen8 {
//定义一个max表示有多少个皇后
int max = 4;
int[] array = new int[max];
static int count = 0;
public static void main(String[] args) {
//测试8皇后是否正确
Queen8 queen8 = new Queen8();
long startTime = System.currentTimeMillis();
queen8.check(0);
long endTime = System.currentTimeMillis();
long costTime = endTime - startTime;
System.out.printf("一共有%d解法",count);
System.out.println();
System.out.println(costTime);
}
//编写方法,放置第n个皇后
private void check(int n){
if (n==max){
print();
return;
}
//依次放入皇后并且判断是否冲突
for (int i = 0; i < max; i++) {
//先把当前这个皇后n,放在该行的第一列
array[n] = i;
//判断当放置到第n个皇后是否冲突
if (judge(n)){
check(n+1);
}
//如果冲突,并且i++,放置到本行的后一个位置
}
}
//查看当我们放置第n个皇后,就去检测该皇后是否和前面拜访的皇后冲突
/**
* @param n 表示第n个皇后
*/
private boolean judge(int n){
for (int i = 0; i < n; i++) {
//Math.abs(n-i) == Math.abs(array[n]-array[i])
//如果在斜线位置,皇后之间列之差就等于行之差
if (array[i] == array[n] ||Math.abs(n-i) == Math.abs(array[n]-array[i])){
return false;
}
}
return true;
}
//方法,将皇后摆放的位置输出
private void print(){
count++;
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
}
二.N皇后之字符串
这个跟上一个八皇后不同的是不再有下标能代表行,值能代表列的数组了,需要自己设置行列,并且考虑怎么才能很好的做合法性判断,还需要正确的添加字符集合。

import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
Solution solution = new Solution();
List<List<String>> solveNQueens = solution.solveNQueens(4);
System.out.println(solveNQueens);
}
}
class Solution {
private List<List<String>> result = new ArrayList<>();// 存储所有合法的棋盘结果
public List<List<String>> solveNQueens(int n) {
List<StringBuilder> board = new ArrayList<>(); // 存储当前棋盘状态(行优先)
for (int i = 0; i < n; i++) {
StringBuilder row = new StringBuilder();
row.append(".".repeat(n)); // 初始化每行都是n个点
board.add(row);
}
backtrack(board, 0); // 从第0行开始回溯
return result;
}
/**
* 回溯函数:尝试在第row行放置皇后
* @param board 当前棋盘状态
* @param row 当前要放置皇后的行号
*/
public void backtrack(List<StringBuilder> board, int row) {
int n = board.size();
// 递归终止:所有行都放置了皇后,保存当前棋盘
if (row == n) {
List<String> validBoard = new ArrayList<>();
for (StringBuilder sb : board) {
validBoard.add(sb.toString());
}
result.add(validBoard);
return;
}
// 遍历当前行的每一列,尝试放置皇后
for (int col = 0; col < n; col++) {
// 检查当前位置(row, col)是否合法
if (!isValid(col, row, board)) {
continue;
}
// 1. 做选择:在(row, col)放皇后
board.get(row).setCharAt(col, 'Q');
// 2. 递归:处理下一行,探索这个选择的所有后续可能性
backtrack(board, row + 1);
// 3. 撤销选择:把皇后拿掉,恢复为点,尝试当前行的下一列
board.get(row).setCharAt(col, '.');
}
}
/**
* 检查(row, col)位置放置皇后是否合法
* @param col 列号
* @param row 行号
* @param board 棋盘状态
* @return 合法返回true,否则false
*/
public boolean isValid(int col, int row, List<StringBuilder> board) {
int n = board.size();
// 1. 检查同一列是否有皇后(只检查当前行上方的行)
for (int i = 0; i < row; i++) {
if (board.get(i).charAt(col) == 'Q') {
return false;
}
}
// 2. 检查左上对角线(行-1,列-1)
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (board.get(i).charAt(j) == 'Q') {
return false;
}
}
// 3. 检查右上对角线(行-1,列+1)
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (board.get(i).charAt(j) == 'Q') {
return false;
}
}
return true;
}
}
三.全排列
这个的思路和八皇后类似,但要思考什么作为合法性判断来缩小范围,什么作为递归出来的条件,我的选择,递归,回撤该怎么做。

class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] user;
public List<List<Integer>> permute(int[] nums) {
user = new boolean[nums.length];
backTrack(nums);
return result;
}
public void backTrack(int[] nums){
if(path.size()==nums.length){
result.add(new ArrayList<>(path));
return;
}
for(int i = 0;i<nums.length;i++){
if(!user[i]){
path.add(nums[i]);
//选择
user[i]=true;
//递归
backTrack(nums);
//回撤
path.remove(path.size()-1);
user[i]=false;
}
}
}
}
四.总结
第一个题是看韩顺平老师的数据结构课程跟着敲出来的,第二三题,我是根据ai给出思路和代码,慢慢理解敲出来的,虽然惭愧没完整自己想出一题,但自己写了三题目有一点心得写在这里。
以后会越来越好。

665

被折叠的 条评论
为什么被折叠?



