回溯法经典实战:0/1 全排列与 N 皇后问题(递归 + 非递归双实现)

摘要 :本文详细讲解回溯法两大经典入门问题 ------0/1 全排列生成N 皇后问题 ,分别用递归和 ** 非递归(迭代)** 两种方式实现 C++ 代码,对比两种写法的核心逻辑,帮助彻底掌握回溯法思想。


一、前言:回溯法核心思想

回溯法本质是试探 + 回退的暴力搜索优化:

  1. 按路径尝试所有可能解;
  2. 发现当前路径不合法 / 无解时,回退到上一步重新选择;
  3. 直到找到所有合法解。

本文用两个经典案例落地:

  • 基础案例:生成 n 位 0/1 全排列
  • 经典案例:N 皇后问题(算法必考题)

二、案例 1:0/1 全排列生成

1. 问题描述

生成长度为 n 的所有 0/1 组合,例如 n=3 时,输出:

复制代码
000、001、010、011、100、101、110、111

2. 递归实现(直观易写)

递归思路:每一位选 0 或选 1,直到填满所有位。

cpp 复制代码
// 递归生成0/1全排列
void PrintO1(int* X, int i, int n)
{
    // 递归终止:填满所有位,打印结果
    if (i > n)
    {
        for (int j = 1; j <= n; ++j)
        {
            printf("%2d", X[j]);
        }
        printf("\n------------\n");
    }
    else
    {
        X[i] = 1;   // 第i位选1
        PrintO1(X, i + 1, n);
        X[i] = 0;   // 回溯:第i位选0
        PrintO1(X, i + 1, n);
    }
}

3. 非递归实现(效率更高)

循环 + 栈思想模拟递归回溯,避免递归栈溢出。

cpp 复制代码
// 打印数组
void Print(const int* X, int n)
{
    for (int i = 1; i <= n; ++i)
    {
        printf("%3d", X[i]);
    }
    printf("\n----------------------\n");
}

// 非递归生成0/1全排列
void Print01(int* X, int n)
{
    int k = 1;
    X[k] = 2;  // 初始值设为2,通过减1进入0/1范围
    while (k > 0)
    {
        X[k] -= 1;
        if (X[k] >= 0)  // 当前位为0/1,合法
        {
            if (k == n) 
            {
                Print(X, n);  // 填满所有位,输出
            }
            else
            {
                ++k;          // 处理下一位
                X[k] = 2;
            }
        }
        else  // 当前位无合法值,回溯
        {
            --k;
        }
    }
}

4. 测试代码

cpp 复制代码
int main()
{
    int X[] = { 0,0,0,0 };
    Print01(X, 3);  // 生成3位0/1全排列
    return 0;
}

三、案例 2:N 皇后问题(算法面试高频)

1. 问题描述

n×n棋盘上放n个皇后,满足:

  • 任意两个皇后不同行、不同列、不同对角线

2. 核心判断函数

判断第t行皇后位置是否合法:

cpp 复制代码
bool Place(int t)
{
    // 检查与前t-1行是否冲突
    for (int j = 1; j < t; ++j)
    {
        // 同列 或 同对角线
        if (x[j] == x[t] || abs(t - j) == abs(x[t] - x[j]))
        {
            return false;
        }
    }
    return true;
}

3. 递归回溯实现

cpp 复制代码
class Queen
{
private:
    int n = 0;
    vector<int> x;  // x[i]表示第i行皇后所在列
    int sum = 0;    // 解的总数

    void PrintQueen();  // 打印棋盘
    bool Place(int t);  // 位置合法性判断
    void Backtrack(int i);  // 递归回溯

public:
    int nQueen(int qn)
    {
        n = qn;
        if (n <= 3) return 0;  // n≤3无解
        x.resize(n + 1, 0);
        Backtrack(1);
        return sum;
    }
};

// 递归回溯核心
void Queen::Backtrack(int i)
{
    if (i > n)  // 找到一组解
    {
        sum++;
        PrintQueen();
    }
    else
    {
        // 遍历第i行所有列
        for (int j = 1; j <= n; ++j)
        {
            x[i] = j;
            if (Place(i))  // 位置合法,继续下一行
            {
                Backtrack(i + 1);
            }
        }
    }
}

4. 非递归回溯实现(推荐)

用循环模拟递归,性能更优、无栈溢出风险

cpp 复制代码
class Queen2
{
private:
    int n = 0;
    vector<int> x;
    int sum = 0;

    void PrintQueen();
    bool Place(int t);
    void Backtrack();  // 非递归回溯

public:
    int nQueen(int qn)
    {
        n = qn;
        if (n <= 3) return 0;
        x.resize(n + 1, 0);
        Backtrack();
        return sum;
    }
};

// 非递归核心
void Queen2::Backtrack()
{
    x[1] = 0;
    int k = 1;  // 当前处理第k行
    while (k > 0)
    {
        x[k]++;  // 尝试下一列
        // 跳过非法列
        while (x[k] <= n && !Place(k)) x[k]++;
        
        if (x[k] <= n)  // 找到合法列
        {
            if (k == n)  // 找到完整解
            { 
                sum++; 
                PrintQueen(); 
            }
            else  // 处理下一行
            {
                k++;
                x[k] = 0;
            }
        }
        else  // 当前行无合法解,回溯
        {
            k--;
        }
    }
}

5. 测试 4 皇后问题

cpp 复制代码
int main()
{
    Queen2 qu;
    int num = qu.nQueen(4);
    cout << "4皇后解的个数:" << num << endl;
    return 0;
}

输出结果 :4 皇后有2 个合法解


四、递归 vs 非递归 对比

对比维度 递归实现 非递归实现
代码简洁度 极高,逻辑直观 稍复杂,需手动控制回溯
性能 有函数调用开销 无开销,效率更高
稳定性 n 过大可能栈溢出 无栈溢出风险
适用场景 算法学习、小规模数据 工程落地、大规模数据

五、总结

  1. 回溯法核心:试探→验证→回溯→再试探
  2. 0/1 全排列是回溯法入门模板,掌握选 / 不选逻辑;
  3. N 皇后是回溯法经典应用,重点理解位置合法性判断;
  4. 递归适合写算法题,非递归适合实际工程使用。

文末福利

完整可运行代码已整合,直接复制即可编译运行:

cpp 复制代码
// 全文整合代码(含0/1全排列+N皇后双实现)
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

// 递归0/1全排列
void PrintO1(int* X, int i, int n)
{
    if (i > n)
    {
        for (int j = 1; j <= n; ++j)
            printf("%2d", X[j]);
        printf("\n------------\n");
    }
    else
    {
        X[i] = 1; PrintO1(X, i+1, n);
        X[i] = 0; PrintO1(X, i+1, n);
    }
}

// 打印函数
void Print(const int* X, int n)
{
    for (int i=1; i<=n; ++i)
        printf("%3d", X[i]);
    printf("\n----------------\n");
}

// 非递归0/1全排列
void Print01(int* X, int n)
{
    int k=1; X[k]=2;
    while(k>0)
    {
        X[k]--;
        if(X[k]>=0)
        {
            if(k==n) Print(X,n);
            else {k++; X[k]=2;}
        }
        else k--;
    }
}

// N皇后非递归实现
class Queen2
{
private:
    int n; vector<int> x; int sum;
    bool Place(int t)
    {
        for(int j=1; j<t; j++)
            if(x[j]==x[t] || abs(t-j)==abs(x[t]-x[j]))
                return false;
        return true;
    }
    void PrintQueen()
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
                printf(x[i]==j?" Q":" #");
            printf("\n");
        }
        printf("\n----------------\n");
    }
    void Backtrack()
    {
        x[1]=0; int k=1;
        while(k>0)
        {
            x[k]++;
            while(x[k]<=n && !Place(k)) x[k]++;
            if(x[k]<=n)
            {
                if(k==n) {sum++; PrintQueen();}
                else {k++; x[k]=0;}
            }
            else k--;
        }
    }
public:
    int nQueen(int qn)
    {
        n=qn; sum=0;
        if(n<=3) return 0;
        x.resize(n+1,0);
        Backtrack();
        return sum;
    }
};

int main()
{
    cout<<"===== 3位0/1全排列 =====\n";
    int X[]={0,0,0,0};
    Print01(X,3);

    cout<<"\n===== 4皇后问题 =====\n";
    Queen2 qu;
    cout<<"解的个数:"<<qu.nQueen(4)<<endl;
    return 0;
}

💡 原创不易,欢迎点赞、收藏、关注,后续会更新更多算法实战干货!

相关推荐
YuanDaima20482 小时前
队列与单调队列基础原理与题目说明
人工智能·python·算法·leetcode·队列·手撕代码
董董灿是个攻城狮2 小时前
放风的 Claude 你怕不怕
算法
阿杰学AI2 小时前
AI核心知识122—大语言模型之 直接偏好优化(简洁且通俗易懂版)
人工智能·算法·机器学习·ai·强化学习·dpo·直接优化偏好
kyle~2 小时前
BFS(广度优先搜索)与 DFS (深度优先搜索)
c++·算法·深度优先·宽度优先
Hello.Reader2 小时前
算法是什么
linux·运维·算法
炽烈小老头2 小时前
【每天学习一点算法 2026/04/15】两整数之和(附带位运算总结)
学习·算法
Tisfy2 小时前
LeetCode 2515.到目标字符串的最短距离:从中间往两边遍历
算法·leetcode·字符串·题解·数组·遍历
Xpower 173 小时前
PHM算法学习 Day 3:深度卷积神经网络(CNN)故障诊断变体
学习·算法·cnn
苯酸氨酰糖化物3 小时前
基于深度学习(U-Net架构下改良GAN与ViT算法)的高效肺部多模态疾病预测模型
人工智能·深度学习·算法·生成对抗网络·视觉检测