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

LeetCode-44 回溯解法

全排列:一篇看懂回溯解法

题目

给定一个不含重复数字的整数数组nums,返回它的所有全排列。

示例

输入:nums=[1,2,3]输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

一、题目

这道题的核心是:

把数组中的每个数字,都轮流放到当前位置上,直到所有位置都放满。

比如nums = [1,2,3]

  • 第一位可以放1
  • 第一位也可以放2
  • 第一位还可以放3

每确定一位,就继续递归处理下一位。

这就是典型的回溯问题。


二、最容易想到的思路

可以这样理解:

  • 先确定第0个位置放谁
  • 再确定第1个位置放谁
  • 再确定第2个位置放谁
  • 当所有位置都确定后,就得到一个排列

为了高效,我们通常使用交换的方式,而不是每次都新建数组。


三、回溯解法

代码

importjava.util.ArrayList;importjava.util.List;classSolution{publicList<List<Integer>>permute(int[]nums){List<List<Integer>>res=newArrayList<>();backtrack(nums,0,res);returnres;}privatevoidbacktrack(int[]nums,intfirst,List<List<Integer>>res){if(first==nums.length){List<Integer>list=newArrayList<>();for(intnum:nums){list.add(num);}res.add(list);return;}for(inti=first;i<nums.length;i++){swap(nums,first,i);// 选择一个数放到当前位置backtrack(nums,first+1,res);// 递归处理下一个位置swap(nums,first,i);// 恢复现场}}privatevoidswap(int[]nums,inti,intj){inttemp=nums[i];nums[i]=nums[j];nums[j]=temp;}}

四、为什么这样做可行

假设数组是:

[1,2,3]

第一步:确定第 0 位

  • 1放到第 0 位,数组还是[1,2,3]
  • 2放到第 0 位,数组变成[2,1,3]
  • 3放到第 0 位,数组变成[3,2,1]

第二步:递归确定后面的位

例如已经确定第 0 位是1,那后面就去处理[2,3]的排列。

最后会得到:

  • [1,2,3]
  • [1,3,2]

再继续处理第 0 位是2、第 0 位是3的情况。


五、为什么交换后还要交换回来

这是回溯最关键的一步。

例如当前数组是:

[1,2,3]

当我们尝试把2放到第 0 位时,会执行:

swap(nums,0,1)

数组变成:

[2,1,3]

递归处理完以后,如果不换回来,数组就一直是[2,1,3],会影响后面继续尝试别的情况。

所以必须再交换一次:

swap(nums,0,1)

把数组恢复成原来的样子:

[1,2,3]

这样下一轮尝试才不会出错。

一句话概括:

交换过去是做选择,交换回来是撤销选择。


六、执行过程示例

nums = [1,2,3]为例:

第一层:确定第 0 位

  • 1,得到[1,2,3]
  • 2,得到[2,1,3]
  • 3,得到[3,2,1]

第二层:确定第 1 位

例如当前是[1,2,3]

  • 2,得到[1,2,3]
  • 3,得到[1,3,2]

第三层:确定第 2 位

只剩最后一个数,直接加入结果。

最终得到全部 6 种排列。


七、复杂度分析

时间复杂度

O(n × n!)

原因:

  • 一共有n!种排列
  • 每次加入答案时,需要把当前数组复制到结果中,花费O(n)

空间复杂度

O(n)

主要是递归调用栈的深度。

不计最终答案所占空间。


八、这道题的本质

这道题的本质不是“交换”,而是:

枚举每个位置可以放哪些数。

交换只是一个实现手段,它的优点是:

  • 不需要额外创建很多新数组
  • 代码更简洁
  • 性能更好

九、总结

这道题是经典的回溯题。

可以记住下面这个模板:

做选择 递归 撤销选择

对应到本题就是:

swap(nums,first,i);backtrack(nums,first+1,res);swap(nums,first,i);

其中:

  • swap:把一个数放到当前的位置
  • backtrack:递归处理下一个位置
  • swap:恢复现场,继续尝试别的数

十、结论

全排列问题最经典的解法就是回溯 + 交换

它的核心思想是:

  • 每一层确定一个位置放什么
  • 通过交换把候选数字放到当前位置
  • 递归处理剩余位置
  • 处理完再恢复原数组
http://www.jsqmd.com/news/493371/

相关文章:

  • 【实战】ESP32 + LN298N 驱动编码器推杆:从零搭建位置闭环控制系统
  • 如何在3分钟内通过手机号找回QQ账号:终极快速解决方案
  • 力扣算法刷题 Day 14
  • 3大突破!图像矢量化技术如何解决中小企业设计资源优化难题
  • 抖音批量监控千名博主视频更新,实时下载技术解析
  • Python默认参数详解
  • VS Code 聊天功能深度解析:从激活到精通,解锁AI编程新范式
  • 从保护环设计到势垒高度设置:Silvaco仿真肖特基二极管的3个关键陷阱
  • Task2:ESP32代码学习和基础API需求
  • CLIP-GmP-ViT-L-14在嵌入式设备端的轻量化部署探索
  • 如何用Python实现三角函数公式的自动计算与验证
  • CTF流量分析新选择:3个核心功能让你轻松应对网络安全挑战
  • 从零开始:tModLoader全面指南 - 打造专属泰拉瑞亚模组世界
  • 原本该有一篇文章发出来
  • 从零学 Linux:从发行版到包管理器,一篇吃透基础要点
  • SiameseAOE中文-base参数详解:Prompt+Text构建思路与schema定义规范
  • SecGPT-14B开源模型落地:适配国产化GPU环境的网络安全垂直大模型实践
  • STM32F4实战:CoreMark跑分从移植到优化的完整指南(附常见问题排查)
  • 如何3分钟实现抖音视频批量下载:douyin-downloader完整指南
  • cmux多智能体管理工具
  • 阿里云MQTT连接失败?工程师亲授的PubSubClient避坑指南(附完整参数配置)
  • LSTM与BERT模型在序列标注任务上的分割效果对比
  • dll文件缺失,DirectX 运行库修复工具,一键完成dll缺失修复、解决99.99%程序故障、闪退、卡顿等常见问题,轻松解决
  • 用SDXL 1.0做个人作品集:快速生成多种风格的高质量插画与概念图
  • OFA模型轻量化部署:针对边缘设备的优化思路与探索
  • 从雷诺运输定理到高维PBE:流体动力学中的物质守恒法则
  • Local AI MusicGen批量生成任务的优化策略
  • LangChain4j实战:构建企业级RAG问答系统的核心步骤与避坑指南
  • AI头像生成器GPU算力方案:Qwen3-32B在A10/A100/L4卡上的部署性能对比
  • DIY—一拖四串口调试助手