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

深入理解全排列算法:DFS与回溯的完美结合

全排列问题是算法中的经典问题,其目标是将一组数字的所有可能排列组合列举出来。本文将详细解析如何通过深度优先搜索(DFS)回溯法高效生成全排列,并通过模拟递归过程帮助读者彻底掌握其核心思想。


问题描述

给定一个正整数 n,生成数字 1 到 n 的所有排列。例如,当 n = 3 时,输出应为:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

算法思路

1. 核心变量

  • path[N]:存储当前生成的排列。

  • dt[N]bool 数组):标记数字是否已被使用(避免重复)。

  • n:排列的长度(如 n=3 表示生成 1,2,3 的全排列)。

2. DFS递归函数

void dfs(int u) {if (u == n) {  // 终止条件:排列已填满print_permutation();  // 输出当前排列return;}for (int i = 0; i < n; i++) {if (!dt[i]) {  // 如果数字i未被使用path[u] = i;  // 选择idt[i] = true;  // 标记为已使用dfs(u + 1);   // 递归填充下一位dt[i] = false; // 回溯:恢复状态}}
}

3. 主函数

int main() {scanf("%d", &n);dfs(0);  // 从第0位开始生成排列return 0;
}

递归过程模拟(以n=2为例)

初始状态

  • n = 2(排列长度为 2,数字为 1, 2,对应内部 0, 1)。

  • path = [?, ?](未初始化)。

  • dt = [false, false](初始均未使用)。


递归调用树

第一层调用:dfs(0)
  • 当前位 u = 0(正在填充 path[0])。

  • 循环 i = 0

    1. dt[0] 为 false,选择数字 0(实际输出为 1)。

    2. 更新状态

      • path = [0, ?]

      • dt = [true, false]

    3. 递归进入 dfs(1)

第二层调用:dfs(1)
  • 当前位 u = 1(正在填充 path[1])。

  • 循环 i = 0

    • dt[0] 为 true,跳过。

  • 循环 i = 1

    1. dt[1] 为 false,选择数字 1(实际输出为 2)。

    2. 更新状态

      • path = [0, 1]

      • dt = [true, true]

    3. 递归进入 dfs(2)

第三层调用:dfs(2)
  • 终止条件u == n2 == 2),打印当前排列:

    • 输出 path[0]+1, path[1]+1 → 1 2

  • 返回上一级(回溯到 dfs(1))。

回溯到 dfs(1)
  • 恢复状态

    • dt[1] = falsedt = [true, false])。

  • 循环结束,返回上一级 dfs(0)

回溯到 dfs(0)
  • 恢复状态

    • dt[0] = falsedt = [false, false])。

  • 继续循环 i = 1

    1. dt[1] 为 false,选择数字 1(实际输出为 2)。

    2. 更新状态

      • path = [1, ?]

      • dt = [false, true]

    3. 递归进入 dfs(1)

第二层调用:dfs(1)
  • 当前位 u = 1

  • 循环 i = 0

    1. dt[0] 为 false,选择数字 0(实际输出为 1)。

    2. 更新状态

      • path = [1, 0]

      • dt = [true, true]

    3. 递归进入 dfs(2)

第三层调用:dfs(2)
  • 打印排列:path[0]+1, path[1]+1 → 2 1

  • 返回上一级(回溯到 dfs(1))。

回溯到 dfs(1)
  • 恢复 dt[0] = falsedt = [false, true])。

  • 循环结束,返回 dfs(0)

回溯到 dfs(0)
  • 恢复 dt[1] = falsedt = [false, false])。

  • 循环结束,程序终止。

最终输出

1 2 
2 1 

关键步骤总结

  1. 递归向下:逐层选择未被使用的数字,更新 path 和 dt

  2. 回溯向上:在每一层递归返回时恢复 dt 的状态,确保其他分支能正确使用数字。

  3. 终止条件:当 path 填满时立即输出结果。


递归树图示

dfs(0)
│
├─ i=0 (选1)
│  ├─ dfs(1)
│  │  ├─ i=1 (选2) → dfs(2) → 输出 [1, 2]
│  │  └─ 回溯:释放2
│  └─ 回溯:释放1
│
└─ i=1 (选2)├─ dfs(1)│  ├─ i=0 (选1) → dfs(2) → 输出 [2, 1]│  └─ 回溯:释放1└─ 回溯:释放2

关键点总结

  1. DFS的作用:递归地尝试所有可能的数字选择,直到填满整个排列。

  2. 回溯的必要性:在递归返回时恢复 dt 数组的状态,确保后续分支能正确选择数字。

  3. 时间复杂度:O(N×N!),因为共有 N! 种排列,每种排列需要 O(N) 时间生成。


优化与扩展

  1. 非递归实现:可以用栈模拟递归,避免递归深度过大(但对小规模 n 影响不大)。

  2. 字典序排列:调整循环顺序,使输出按字典序排列。

  3. 去重排列:如果输入包含重复数字,需额外判断避免重复排列。


完整代码(C语言)

 

       通过DFS和回溯的结合,我们可以高效地生成全排列。理解递归的展开与回溯是掌握该算法的关键。希望本文的逐步解析能帮助你彻底掌握这一经典问题!

相关文章:

深入理解全排列算法:DFS与回溯的完美结合

全排列问题是算法中的经典问题&#xff0c;其目标是将一组数字的所有可能排列组合列举出来。本文将详细解析如何通过深度优先搜索&#xff08;DFS&#xff09;和回溯法高效生成全排列&#xff0c;并通过模拟递归过程帮助读者彻底掌握其核心思想。 问题描述 给定一个正整数 n&a…...

低频rfid手持机,助力动物耳标智能化管理

低频RFID手持机&#xff0c;助力动物耳标智能化管理&#xff0c;正逐步成为现代畜牧业不可或缺的工具。它不仅能够高效读取动物耳标中的信息&#xff0c;如唯一识别码、疫苗接种记录、健康状态等&#xff0c;还极大地提升了数据录入的准确性和时效性。 1.精准识别与追踪‌ 通过…...

【从零开始学习JVM | 第三篇】虚拟机的垃圾回收学习(一)

堆空间的基本结构 Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时&#xff0c;Java 自动内存管理最核心的功能是 堆 内存中对象的分配与回收。 Java 堆是垃圾收集器管理的主要区域&#xff0c;因此也被称作 GC 堆&#xff08;Garbage Collected Heap&am…...

蓝桥杯之门牌

问题&#xff1a; 这条街一共有 2020 位住户&#xff0c;门牌号从 1 到 2020 编号。 小蓝制作门牌的方法是先制作 0 到9 这几个数字字符&#xff0c;最后根据需要将字符粘贴到门牌上&#xff0c;例如门牌 1017 需要依次粘贴字符 1、0、1、7,即需要 1 个字符 0&#xff0c;2 个…...

Jieba分词的原理及应用(三)

前言 “结巴”中文分词&#xff1a;做最好的 Python 中文分词组件 上一篇文章讲了使用TF-IDF分类器范式进行企业级文本分类的案例。其中提到了中文场景不比英文场景&#xff0c;在喂给模型之前需要进行分词操作。 分词的手段有很多&#xff0c;其中最常用的手段还是Jieba库进行…...

Openlayers:flat样式介绍

在前段时间我在使用WebGL矢量图层时接触到了flat样式&#xff0c;我对其十分的感兴趣&#xff0c;于是我花了几天的时间对其进行了了解&#xff0c;在这篇文章中我将简单的介绍一下flat样式的使用方式以及我对其的一些理解。 一、了解flat样式 1.什么是flat样式&#xff1f; …...

149页研读——华为基于IPD全过程研发质量管理【附全文阅读】

本文介绍了IPD(集成产品开发)的全过程研发质量管理,强调了以客户需求为导向,通过跨部门协同、资源整合、快速响应等方式提高研发效率和成功率。文章详细阐述了IPD研发管理体系的精要,包括其核心思想、优势、框架以及核心理念。 其中,跨领域平台与技术研发、端到端流程与项…...

Linux入门指南:从零开始探索开源世界

引言 欢迎来到Linux的奇妙世界&#xff01;&#x1f30d; 这个诞生于1991年的开源操作系统&#xff0c;如今已悄然成为数字世界的隐形支柱。从智能手机到超级计算机&#xff0c;从智能家电到航天器&#xff0c;Linux的身影无处不在。本文将带你纵览Linux的发展历程、主流发行版…...

Oracle 23ai Vector Search 系列之5 向量索引(Vector Indexes)

文章目录 Oracle 23ai Vector Search 系列之5 向量索引Oracle 23ai支持的向量索引类型内存中的邻居图向量索引 (In-Memory Neighbor Graph Vector Index)磁盘上的邻居分区矢量索引 (Neighbor Partition Vector Index) 创建向量索引HNSW索引IVF索引 向量索引示例参考 Windows 环…...

vue模拟扑克效果

vue模拟扑克效果 效果图&#xff1a; step1:C:\Users\wangrusheng\PycharmProjects\untitled18\src\views\Home.vue <template><div class"poker-container"><!-- 使用复合数据对象实现双行显示 --><divv-for"(card, index) in POKER_…...

Android12源码编译之预置Android Studio项目Android.mk文件编写

1、在AndroidManifest.xml文件中添加package"com.sprd.silentinstalldemo"属性&#xff0c;因为新版本的Android Studio默认生成的AndroidManifest.xml是没有这个属性值的 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:an…...

高阶函数/柯里化/纯函数

本篇文章主要是介绍一下标题里面的概念&#xff0c;在面试的时候经常文档&#xff0c;结合阅读到的资料&#xff0c;结合本人的个人见解出品了该文章&#xff0c;如有写的不好的地方或理解有误的&#xff0c;还望阁下多多指教。 1、高阶函数 什么是高阶函数&#xff1f; 接受…...

Spring Boot 测试详解,包含maven引入依赖、测试业务层类、REST风格测试和Mock测试

Spring Boot 测试详解 1. 测试依赖引入 Spring Boot 默认通过以下 Maven 依赖引入测试工具&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</s…...

探索 HTML5 新特性:提升网页开发的现代体验

在 Web 开发的演进历程中&#xff0c;HTML5 无疑是一座重要的里程碑。它不仅为网页带来了更丰富的功能&#xff0c;还提升了开发效率与用户体验。本文将深入探讨 HTML5 那些令人瞩目的新特性&#xff0c;助你紧跟现代 Web 开发潮流。 一、语义化标签&#xff1a;让结构更清晰 …...

Python中如何用正则表达式精准匹配IP地址?

在网络编程和数据处理时&#xff0c;我们经常需要从文本中提取或验证IP地址。Python的正则表达式(re模块)是完成这个任务的利器。但你知道怎么写才能准确匹配各种合法的IP地址吗&#xff1f;今天我们就来详细探讨这个问题。 为什么需要IP正则表达式&#xff1f; 假设你正在分…...

leetcode刷题日记——螺旋矩阵

[ 题目描述 ]&#xff1a; [ 思路 ]&#xff1a; 题目要求按顺时针顺序给出m行n列的矩阵的数组按照题目所给的顺序挨个插入答案数组中运行如下 int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize) {*returnSize matrixSize * matrixCol…...

金字塔原理学习法

金字塔原理学习法 金字塔原理&#xff08;Pyramid Principle&#xff09; 是由麦肯锡顾问芭芭拉明托提出的结构化思维方法&#xff0c;核心是通过纵向分层、横向归类的逻辑架构组织信息&#xff0c;实现复杂问题的清晰表达与高效学习。在技术学习领域&#xff0c;该原理能有效…...

模板引擎语法-标签

模板引擎语法-标签 文章目录 模板引擎语法-标签[toc]一、用于进行判断的{% if-elif-else-endif %}标签二、关于循环对象的{% for-endfor %}标签三、关于自动转义的{% autoescape-endautoescape %}标签四、关于循环对象的{% cycle %}标签五、关于检查值是否变化的{% ifchange %}…...

深度学习学习笔记

目录 摘要 Abstracts 简介 Hourglass Module&#xff08;Hourglass 模块&#xff09; 网络结构 Intermediate Supervision&#xff08;中间监督&#xff09; 训练过程细节 评测结果 摘要 本周阅读了《Stacked Hourglass Networks for Human Pose Estimation》&#xf…...

当Browser Use遇见A2A:浏览器自动化与智能体协作的“冰与火之歌“

——一场正在改写数字文明的技术奇遇 第一章 浏览器革命&#xff1a;从"手动挡"到"自动驾驶" 1.1 传统自动化工具的"中年危机" 还记得2023年那个抓狂的凌晨吗&#xff1f;你蹲守演唱会门票时&#xff0c;Selenium脚本因为验证码识别失败第108次…...

智能医疗辅助诊断:深度解析与实战教程

引言&#xff1a;医疗领域的新革命 在医疗资源紧张、诊断效率亟待提升的今天&#xff0c;智能医疗辅助诊断技术正以前所未有的速度改变医疗行业的面貌。通过结合人工智能与医学专业知识&#xff0c;智能医疗辅助诊断系统能够为医生提供精准的诊断建议和决策支持&#xff0c;显…...

(已解决)如何安装python离线包及其依赖包 2025最新

字数 305&#xff0c;阅读大约需 2 分钟 没有网络的Linux服务器上&#xff0c;如何安装完整的、离线的python包 1. 写入待安装的包 新建requirement.txt, 写入待安装的包 和 包的版本 如 flwr1.13.0 2.使用命令行直接下载 pip download -d flwr_packages -r requirements.tx…...

Java如何获取文件的编码格式?

Java获取文件的编码格式 在计算机中&#xff0c;文件编码是指将文件内容转换成二进制形式以便存储和传输的过程。常见的文件编码格式包括UTF-8、GBK等。不同的编码使用不同的字符集和字节序列&#xff0c;因此在读取文件时需要正确地确定文件的编码格式 Java提供了多种方式以获…...

豪越赋能消防安全管控,解锁一体化内管“安全密码”

在消防安全保障体系中&#xff0c;内部管理的高效运作是迅速、有效应对火灾及各类灾害事故的重要基础。豪越科技凭借在消防领域的深耕细作与持续创新&#xff0c;深入剖析消防体系内部管理的痛点&#xff0c;以自主研发的消防一体化安全管控平台&#xff0c;为行业发展提供了创…...

Python实现链接KS3,并批量下载KS3文件数据到本地

前言 本文是该专栏的第56篇,后面会持续分享python的各种干货知识,值得关注。 在本专栏的上篇文章《Python实现链接KS3,并将文件数据批量上传到KS3》中,笔者有详细介绍基于Python,实现链接KS3并将文件数据批量上传。而本文,笔者将基于在上一篇文章的基础之上,实现链接KS…...

状态机 XState

以下是关于 状态机(XState) 基本知识的梳理,涵盖核心概念、高级特性、实际应用场景及最佳实践,帮助我们掌握这一强大的状态管理工具: 一、状态机核心概念 1. 有限状态机(Finite State Machine, FSM)基础 定义:系统在有限状态集合中流转,由事件触发状态转换核心要素:…...

Python及C++中的排序

一、Python中的排序 &#xff08;一&#xff09;内置排序函数sorted() 基本用法 sorted()函数可以对所有可迭代对象进行排序操作&#xff0c;返回一个新的列表&#xff0c;原列表不会被修改。例如&#xff0c;对于一个简单的数字列表nums [3, 1, 4, 1, 5, 9, 2, 6]&#xff…...

拓扑排序 —— 2. 力扣刷题207. 课程表

题目链接&#xff1a;https://leetcode.cn/problems/course-schedule/description/ 题目难度&#xff1a;中等 相关标签&#xff1a;拓扑排序 / 广度优先搜搜 BFS / 深度优先搜索 DFS 2.1 问题与分析 2.1.1 原题截图 2.1.2 题目分析 首先&#xff0c;理解题目后必须马上意识到…...

从入门到进阶:React 图片轮播 Carousel 的奇妙世界!

全文目录&#xff1a; 开篇语&#x1f590; 前言✨ 目录&#x1f3af; 什么是图片轮播组件&#xff1f;&#x1f528; 初识 React 中的轮播实现示例代码分析 &#x1f4e6; 基于第三方库快速实现轮播示例&#xff1a;用 react-slick优势局限性 &#x1f6e0;️ 自己动手实现一个…...

【STM32】ST7789屏幕驱动

目录 CubeMX配置 配置SPI 开DMA 时钟树 堆栈大小 Keil工程配置 添加两个group 添加文件包含路径 驱动编写 写单字节函数 写字函数 写多字节函数 初始化函数 设置窗口函数 情况一&#xff1a;正常的0度旋转 情况二&#xff1a;顺时针90度旋转 情况三&#xff1…...