回溯法求解N皇后问题

目录

前言

一、回溯法是什么?

二、N皇后问题描述

分析解题思路

三、算法设计

1、递归法

2、非递归法

总结


前言

本文将从递归形式和非递归形式两种方法来介绍求解N皇后问题的回溯法,后续也会更新更多有关算法分析这方面的问题欢迎大家关注~🤩


一、回溯法是什么?

  1. 定义: 回溯法(Backtracking)是一种通过试探性搜索 来解决问题的算法思想,主要用于解决组合问题、决策问题和枚举问题
  2. 核心思想: **"尝试-回退"------**通过逐步构建可能的解,并在发现当前路径无法得到有效解时回退(回溯),尝试其他路径。

通俗的来讲其实回溯法就是一种更高效的穷举方法,而高效就体现在下面三种核心特点中:

  • 系统性搜索:按特定顺序(如深度优先)枚举所有可能的解。
  • 剪枝优化:在搜索过程中提前终止无效分支(如不满足约束条件时)。
  • 递归实现:通常用递归实现试探和回退步骤。

二、N皇后问题描述

N 皇后问题是指在 n * n 的棋盘上要摆 n 个皇后

要求:任何两个皇后++不同行,不同列也不在同一条斜线上++,求给一个整数 n ,返回 n 皇后的摆法数。

例如当输入4时,对应的返回值为2,对应的两种四皇后摆位如下图所示:

分析解题思路

我们可以先固定放入一个皇后,然后再根据条件放入另一个皇后,如果我们发现放到后面已经没有位置满足放皇后的条件了就说明我们前面的摆放肯定是有误,因此我们就要返回错误的一步重新摆放皇后,也就是回溯法的基本思想。

这里要求++不同行,不同列也不在同一条斜线上,++ 那我们就可以固定每个皇后的行分别为1~n ,然后我们再挑选不同的列放入皇后 并且++同时满足不在同一条斜线上++

那其实这里提到的不同列不同斜线就是我们的剪枝条件,我们在搜索的过程中将不满足条件的枝条减去,大大提升了搜索效率;下面是剪枝条件函数的定义:

在使用回溯法的时候,我们通常要定义全局变量,这样方便我们使用递归调用,不需要频繁的传参,这里我们使用x[ i ](i=1、2、3····n)来表示第i行的皇后放在第x[ i ]列,也就是用下标表示行号,值表示列号

cs 复制代码
bool Place(int t) {
    for (int i = 1; i < t; i++) {
        if ((abs(x[t] - x[i]) == abs(t - i)) || (x[t] == x[i]) )
            return false;
    }
    return true;
}

如图所示,如果两个皇后在同一条直线上,则她们的横纵坐标之差的绝对值应该相同。如果两个条件有一个不满足我们就返回false,说明不需要再向下查找了,需要修改当前皇后的位置。

三、算法设计

上面我们了解了怎么剪掉不满足条件的分支,那么我们怎么判断我们找到了一个可行解呢?

还是以四个皇后为例:

如图所示,我们易知假如把1皇后放在第一列,则2皇后不能放在1、2列,可以放在第三列,但此时再往后放3皇后的时候我们发现不管放在哪里都是错误的,则此时我们应该回到2皇后处,再放到第四列尝试,后面的以此类推(大家可以自己在纸上画一画);那么最后我们能够找到一个可行解就是找到了最后的叶节点,此时每个皇后都有位置

1、递归法

cs 复制代码
void Backtrack(int t) {
    if (t > n) {
        sum++;//说明此时已经找到一个可行解
    } else {
        for (int i = 1; i <= n; i++) {
            x[t] = i;
            if (Place(t))
                Backtrack(t + 1);//如果满足条件就继续往下搜索
          //假如不满足条件了,则返回,从这里开始进入循环,判断下一列是否满足条件


        }
    }

}

2、非递归法

传递的参数可以根据题目灵活调整,这里用k表示第k行

cs 复制代码
void Backtrack(int n) {
    x[1] = 0;
    k = 1;
    while (k > 0) {
        x[k] += 1;
        while (x[k] <= n && !Place(k))
            x[k]++;//一直往后加直到找到能够放置的位置
        if (x[k] <= n) {//没有超出可排的范围
            if (k == n) //找到了一个解
                sum++;
            else {
                k++;
                x[k] = 0;//每次都从第一列开始查找
            }
        } else
            k--;//如果都排到外面去了说明该行没有可防止的位置,回溯
    }
}

总结

回溯法是一种通过系统性试探和剪枝优化 来高效穷举所有可能解的算法,其核心思想是"尝试-回退",在解决组合问题时能显著减少无效搜索。以N皇后问题为例,算法通过递归或迭代逐行放置皇后,并利用约束条件(列、斜线冲突)剪枝,避免不必要的路径探索,从而在O(n!)的理论复杂度下实现实际高效求解。 回溯法不仅适用于N皇后这类经典问题,还可推广至数独、图着色等场景,体现了"智能穷举"的算法设计思想,其平衡了代码简洁性(递归)与执行效率(迭代),是解决NP难问题的重要工具。

相关推荐
YKPG18 分钟前
C++学习-入门到精通-【7】类的深入剖析
c++·学习·算法
vivo互联网技术39 分钟前
vivo官网APP首页端智能业务实践
前端·深度学习·算法
JK0x0740 分钟前
代码随想录算法训练营 Day49 图论Ⅰ 深度优先与广度优先
算法·深度优先·图论
baivfhpwxf20231 小时前
在C#中对List<T>实现多属性排序
windows·c#·list
maozexijr1 小时前
Flink的时间问题
javascript·算法·flink
码观天工1 小时前
C#线程池核心技术:从原理到高效调优的实用指南
性能优化·c#·.net·线程·线程池·多线程·异步
LDG_AGI1 小时前
【深度学习】多目标融合算法(六):渐进式分层提取模型PLE(Progressive Layered Extraction)
人工智能·深度学习·神经网络·算法·机器学习·推荐算法
奔跑的废柴2 小时前
LeetCode 925. 长按键入 java题解
java·算法·leetcode·双指针
姬公子5212 小时前
leetcode hot100刷题日记——7.最大子数组和
c++·算法·leetcode
闻缺陷则喜何志丹2 小时前
【回溯 剪支 状态压缩】# P10419 [蓝桥杯 2023 国 A] 01 游戏|普及+
c++·算法·蓝桥杯·剪枝·回溯·洛谷·状态压缩