当前位置: 首页 > news >正文

Java实现N皇后问题的双路径探索:递归回溯与迭代回溯算法详解

N皇后问题要求在N×N的棋盘上放置N个皇后,使得她们无法互相攻击。本文提供递归和循环迭代两种解法,并通过图示解释核心逻辑。


一、算法核心思想

使用回溯法逐行放置皇后,通过冲突检测保证每行、每列、对角线上只有一个皇后。发现无效路径时回退到上一状态。

冲突检测条件(关键):
  1. 同列冲突:col[i] == col[j]
  2. 对角线冲突:abs(col[i] - col[j]) == abs(i - j)

二、递归版本实现

  1. 流程图

  1. 时序图

  1. 代码解释

  1. 程序目的
    该代码用于解决N皇后问题,即在N×N棋盘上放置N个皇后,使其互不攻击(即任意两个皇后不在同一行、列或对角线上),返回所有可能的解。
  2. solveNQueens方法
    • 初始化结果列表result,用于存储所有解。
    • 调用backtrack方法启动递归回溯过程。
    • 参数n表示棋盘大小,cols数组记录每行皇后所在的列位置(索引为行,值为列)。
  3. backtrack递归核心
    • 终止条件:当row == n时,表示所有皇后已正确放置,将当前cols数组的拷贝加入结果(避免后续修改影响结果)。
    • 递归逻辑:遍历当前行的每一列col,若位置有效则放置皇后(cols[row] = col),并递归处理下一行(row + 1)。
  4. isValid冲突检查
    • 检查当前列col与之前所有行(0row-1)的皇后是否冲突:
      • 列冲突cols[i] == col,即同一列已有皇后。
      • 对角线冲突Math.abs(col - cols[i]) == row - i,即行差等于列差绝对值。
    • 若任一条件满足,返回false,否则位置有效。
  5. 主函数测试与输出
    • 测试n=4的皇后问题,调用solveNQueens获取所有解。
    • 使用Lambda表达式遍历打印每个解(如[1, 3, 0, 2]表示第0行皇后在1列,第1行在3列等)。
  6. 关键实现细节
    • 回溯剪枝:仅尝试有效位置,减少递归次数。
    • 结果存储:每次成功时创建新的ArrayList保存当前解,避免引用问题。
    • 递归层次:每层递归处理一行皇后,确保每行只有一个皇后。

示例输出:
对于n=4,输出两个解:[1, 3, 0, 2][2, 0, 3, 1],对应两种不同的棋盘布局方式。

  1. 代码实现
    1. 注意:下标从0开始
import java.util.ArrayList;
import java.util.List;

public class NQueensRecursive {
    public static List<List<Integer>> solveNQueens(int n) {
        List<List<Integer>> result = new ArrayList<>();
        backtrack(n, 0, new Integer[n], result);
        return result;
    }

    private static void backtrack(int n, int row, Integer[] cols, List<List<Integer>> result) {
        if (row == n) {
            result.add(new ArrayList<>(List.of(cols)));
            return;
        }
        for (int col = 0; col < n; col++) {
            if (isValid(cols, row, col)) {
                cols[row] = col;
                backtrack(n, row + 1, cols, result);
            }
        }
    }

    private static boolean isValid(Integer[] cols, int row, int col) {
        for (int i = 0; i < row; i++) {
            if (cols[i] == col || Math.abs(col - cols[i]) == row - i) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        List<List<Integer>> solutions = solveNQueens(4);
        solutions.forEach(System.out::println);
    }
}


三、循环迭代版本实现

  1. 时序图

  1. 流程说明
  1. 初始化阶段
    • 清空皇后位置数组
    • 从第一个皇后开始放置(j=1)
  2. 主循环逻辑
    • 每次循环尝试将当前皇后的位置右移一格
    • 通过check()验证当前位置是否:
      • 与已有皇后同列
      • 处于同一斜线
    • 合法时继续处理下一个皇后,非法时继续右移
    • 当完成最后一列放置时输出有效方案
  3. 回溯机制
    • 当某列所有位置尝试失败后
    • 重置当前列位置为0
    • 回溯到前一列继续尝试新位置
  4. 终止条件
    • 当回溯到j=0时说明所有可能性已穷尽
    • 程序正常结束

该实现通过非递归方式实现了经典的回溯算法,使用while循环和位置重置机制替代了递归调用栈,具有较好的空间效率。

  1. 代码实现
    1. 注意:下标从1开始
public class Test {
    // 定义4个皇后
    static final  Integer N = 4;
    // 存储皇后的列号
   static int[] q =  new int[N+1];
   // 方案数
   static int answer =0;

    public static void main(String[] args) {
        queen();

    }
    public static boolean check(int j){
        for(int i=1;i<j;i++){
            if(q[i]==q[j] || Math.abs(i-j)==Math.abs(q[i]-q[j])){ // 判断是否同列或同一斜线
            return false;
            }
        }
        return true;
    }
    public static void queen(){

       queenInit();
       int j = 1; // 表示正在摆放第j个皇后
        while(j>=1){

            q[j] ++;// 让第j个皇后的位置加1
            while(!check(j) && q[j]<=N){ // 不满足条件的话,就一直向后移动,为了防止越界,还需要判断q[j]的长度
                q[j]++;
            }
            // 判断
            if(q[j]<=N){// 表示第j个位置,合法

                if(j==N){// 最后一个皇后已经摆放完毕了
                    answer++;
                    System.out.print("方案"+answer);
                    System.out.print("结果为:");
                    for (int i = 1; i <=N ; i++) {
                        System.out.print(q[i]+",");
                    }
                    System.out.println();
                }else{// 继续找下一个皇后的摆放位置
                    j++;
                }
            }else{
                // 没有位置摆放了,需要回溯到上一次。
                q[j]=0; // 重置位置
                j--;
            }
        }
    }

    // 皇后初始化
    public static void queenInit(){
        for(int i=1;i<=N;i++){
            q[i]=0;
        }
    }
}



四、算法流程图示(以4皇后为例)

步骤分解:
1. 放置行0的皇后在列0
   Q . . .
   . . . .
   . . . .
   . . . .

2. 行1尝试列0(冲突)→ 尝试列1(冲突)→ 列2有效
   Q . . .
   . . Q .
   . . . .
   . . . .

3. 行2尝试所有列均冲突,回溯到行1
   Q . . .
   . . . . ← 行1无法继续
   . . . .
   . . . .

4. 行1移动到列3
   Q . . .
   . . . Q
   . . . .
   . . . .

5. 行2尝试列1有效
   Q . . .
   . . . Q
   . Q . .
   . . . .

6. 行3无有效列,回溯→最终找到解:
   [1, 3, 0, 2] 对应棋盘:
   . Q . .
   . . . Q
   Q . . .
   . . Q .

五、算法对比

特性递归版本循环迭代版本
代码复杂度简洁,易理解需手动管理状态栈
内存消耗有栈深度限制显式栈结构,可控性强
适用场景小规模数据,代码可读性优先避免栈溢出,大数据量更稳定

两种方法时间复杂度均为O(N!),实际效率相近,选择依据具体场景需求。

http://www.dtcms.com/a/113341.html

相关文章:

  • 【微机及接口技术】- 第四章 内部存储器及其接口(中)
  • LlamaIndex实现RAG增强:上下文增强检索/重排序
  • 我是如何写作的?
  • LintCode第974题-求矩阵各节点的最短路径(以0为标准)
  • 如何将本地更改的README文件同步到自己的GitHub项目仓库
  • OmniParser: 让大模型化身“电脑管家”
  • 洛谷 P3214 [HNOI2011] 卡农
  • 2.IO流的体系和字节输出流FileOutputStream的基本用法
  • macos 魔搭 模型下载 Wan-AI ComfyUI
  • L2-024 部落 #GPLT,并查集 C++
  • 智能驾驶中预测模块简介
  • 广州t11基地顺利完成交割,TCL华星技术产能双升级
  • 【java】Class.newInstance()
  • 硬币找零问题
  • 特征值与特征向量:从理论到应用的全面解析
  • Java类加载问题
  • STM32单片机入门学习——第16节: [6-4] PWM驱动LED呼吸灯PWM驱动舵机PWM驱动直流电机
  • 《AI大模型应知应会100篇》第4篇:Transformer架构深入浅出:大模型的基石
  • cadence17.4和16.6同时安装无法使用的问题
  • 关于图片分类任务的猜想 | 撰写论文 paper
  • .net多贸易公司借入借出归还设备进销存管理系统软件租金计算库存管理
  • M芯片,能运行普通应用程序的原架构虚拟机
  • Java的Selenium元素定位-xpath
  • LeetCode热题100记录-【二叉树】
  • 【Deep Reinforcement Learning Hands-On Third Edition】【第1章:什么是强化学习】
  • 《海空重力测量理论方法及应用》之一重力仪系统组成及工作原理(下)
  • qt designer 软件主题程序设计
  • Python精进系列:从 __name__ 开始了解 python 常见内置变量
  • synchronized 锁升级机制详解
  • ROS2 多机时间同步(Chrony配置简明指南)