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

深入浅出深度优先搜索(DFS)——以经典N皇后问题为例

引言:当算法遇见棋盘

想象你面前有一张 8 ∗ 8 8 * 8 88的国际象棋棋盘,现在需要摆放 8 8 8个皇后,使得任意两个皇后都无法互相攻击。这个看似简单的谜题,自 1848 1848 1848年被提出以来吸引了无数数学家与计算机科学家的目光。当我们把棋盘扩展到 N ∗ N N*N NN规格时,就诞生了著名的N皇后问题。本文将带您通过这个经典问题,深入理解深度优先搜索(DFS)这一重要的算法思想。


一、深度优先搜索原理剖析

1.1 算法核心思想

深度优先搜索(Depth-First Search)采用"不撞南墙不回头"的策略,其运行过程就像探险者深入洞穴:

  • 选择一条路径走到尽头
  • 遇到死胡同后回退到最近分叉点
  • 尝试其他未探索的分支
  • 重复直到找到解或遍历所有可能

1.2 算法实现框架

def dfs(当前状态):
    if 到达终止条件:
        记录/处理结果
        return
    
    for 所有可能的选择:
        if 选择合法:
            做出选择
            dfs(新状态)
            撤销选择

1.3 算法特性分析

  • 时间复杂度 O ( b d ) O(b^d) O(bd)(b为分支因子,d为最大深度)
  • 空间复杂度 O ( d ) O(d) O(d)(递归栈深度)
  • 优势:实现简单,适合寻找所有解
  • 局限:可能陷入深度路径效率低下

二、N皇后问题建模

2.1 问题描述

N ∗ N N*N NN的棋盘上放置 N N N个皇后,要求:

  1. 每行恰好一个皇后
  2. 每列最多一个皇后
  3. 每条对角线最多一个皇后

2.2 问题转化

将棋盘建模为状态空间树:

  • 树节点:已放置 k k k个皇后的部分解
  • 树边:在第 k + 1 k+1 k+1行放置皇后的选择
  • 叶子节点:有效解或冲突状态

三、DFS解法的实现

ACWing N皇后问题

3.1 关键数据结构

#include <iostream>
using namespace std;

const int MAX_N = 10; // 最大棋盘尺寸

int board[MAX_N][MAX_N] = {0}; // 棋盘状态,0表示空,1表示皇后
int col_used[MAX_N] = {0};     // 列占用标记

// 打印当前棋盘状态
void print_board(int n) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cout << (board[i][j] ? "Q" : ".");
        }
        cout << endl;
    }
    cout << endl;
}

// 检查当前位置(x,y)是否安全
bool is_safe(int x, int y, int n) {
    // 检查两条对角线
    for (int i = 1; i < x; i++) {
        // 左上到右下对角线
        if (y - x + i > 0 && board[i][y - x + i]) return false;
        // 右上到左下对角线
        if (y + x - i <= n && board[i][y + x - i]) return false;
    }
    return true;
}

// 递归求解N皇后问题
void find_queen(int row, int placed, int n) {
    // 找到一组解
    if (placed == n) {
        print_board(n);
        return;
    }

    // 尝试在当前行的每一列放置皇后
    for (int col = 1; col <= n; col++) {
        // 跳过已被占用的列
        if (col_used[col]) continue;
        
        // 检查对角线是否安全
        if (!is_safe(row, col, n)) continue;

        // 放置皇后
        board[row][col] = 1;
        col_used[col] = 1;

        // 递归处理下一行
        find_queen(row + 1, placed + 1, n);

        // 回溯:移除皇后
        board[row][col] = 0;
        col_used[col] = 0;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    
    // 参数说明:
    // 1: 从第1行开始
    // 0: 当前已放置0个皇后
    // n: 棋盘大小
    find_queen(1, 0, n);
    
    return 0;
}

3.2 核心优化技巧

  1. 状态压缩:使用三个布尔数组分别记录列和两个对角线的占用情况
  2. 即时剪枝:在放置每个皇后时立即检测冲突,避免无效搜索
  3. 对称性优化:利用棋盘的对称性减少重复计算(示例代码未体现,可扩展)

四、算法执行过程演示

以4皇后问题为例,其搜索过程大致如下入所示,即不断试探,只要没有冲突就进入下一个状态,有冲突则回到上一个状态,知道找到一个解:
在这里插入图片描述


五、复杂度分析与优化方向

5.1 时间复杂度

  • 最坏情况: O ( N ! ) O(N!) O(N!)
  • 实际运行:通过剪枝大幅优化

5.2 空间复杂度

  • O ( N ) O(N) O(N)(递归深度最多N层)

5.3 进阶优化思路

  1. 位运算优化:使用比特位表示状态
  2. 镜像剪枝:利用棋盘对称性减少计算量
  3. 并行计算:分治策略结合多线程

六、DFS的广泛应用场景

  1. 组合优化:全排列、子集生成
  2. 路径查找:迷宫求解、图连通性
  3. 游戏AI:围棋、象棋走法生成
  4. 依赖解析:软件包依赖管理

结语:算法之美的永恒追求

通过N皇后问题,我们不仅领略了DFS的精妙,更体会到算法设计中"暴力与优雅"的完美平衡。从19世纪的手工计算到现代计算机的毫秒级求解,算法的演进史正是人类智慧的见证。当您下次看到棋盘时,或许会会心一笑——这64个方格中,蕴藏着整个计算机科学的缩影。


思考题:如何修改算法使其可以统计解的个数而不记录具体位置?这会带来哪些性能提升?

希望这篇博客能帮助您在算法学习的道路上继续前行,下一站我们将探索广度优先搜索(BFS)的奇妙世界!


http://www.kler.cn/a/536075.html

相关文章:

  • 【大数据技术】搭建完全分布式高可用大数据集群(Scala+Spark)
  • 深入理解linux中的文件(下)
  • Word List 2
  • SQL条件分支中的大讲究
  • 基于RTOS的STM32游戏机
  • ONLYOFFICE 文档 8.3 已发布:PDF 图章、合并形状、更多格式支持等
  • 探索元宇宙:Facebook 如何重塑社交生态
  • A Comprehensive Study on Text-attributed Graphs: Benchmarking and Rethinking
  • 第3章《VTK可视化基础》
  • 蓝桥杯准备 【入门3】循环结构
  • Axure大屏可视化动态交互设计:解锁数据魅力,引领决策新风尚
  • 代码随想录day30
  • 行测智能组卷【61分】
  • ctf网络安全题库 ctf网络安全大赛答案
  • 基于微信小程序的医院预约挂号系统的设计与实现
  • 如何下载B站视频到本地
  • docker,k8s,docker compose三者的关系
  • Kafka 入门与实战
  • (2025,推理语言模型 / RLM,deepseek-v3,推理结构,推理策略,强化学习概念,监督学习方法,计算优化技术)
  • OBS::Display
  • GlusterFS源码讲解:如何实现最终一致性
  • 【实用技能】如何将 Web 视图添加到 Compose Multiplatform 应用程序
  • Java项目: 基于SpringBoot+mybatis+maven+mysql实现的智能学习平台管理系(含源码+数据库+毕业论文)
  • Web3 跨链技术:构建互联互通的虚拟世界
  • C++Primer 赋值运算符
  • MyBatis框架详解