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

Day25| 491.递增子序列、46.全排列、47.全排列 II、回溯总结

文章链接

491.递增子序列

哈希去重:HashSet 保证:每一层只尝试一个值。

剪枝逻辑:

  • 如果当前 nums[i] 比 path 中最后一个元素小,说明不是递增序列,跳过;

  • 如果 nums[i] 已经在当前 for 循环层尝试过了,也跳过(避免重复分支)。

回溯核心思想是:从左到右选择一个数,然后递归往后选更多数,形成子序列。

  • 对于每一个位置 i,有两种选择:

  • 选:将 nums[i] 加入 path;

  • 不选:跳过 nums[i],进入下一个元素;

  • 递归时带一个 startIndex,保证从左到右、不回头。

public class Solution {public IList<IList<int>> res=new List<IList<int>>();public IList<int> path=new List<int>();public IList<IList<int>> FindSubsequences(int[] nums) {BackTracking(nums,0);return res;}public void BackTracking(int[] nums,int startIndex){if(path.Count>=2){res.Add(new List<int>(path));}HashSet<int> hs=new HashSet<int>();for(int i=startIndex;i<nums.Length;i++){if(path.Count>0&&path[path.Count-1]>nums[i]||hs.Contains(nums[i])){continue;}hs.Add(nums[i]);path.Add(nums[i]);BackTracking(nums,i+1);path.RemoveAt(path.Count-1);}}
}

46.全排列

步骤内容
回溯结构逐个选取数字,构造出一个完整路径
状态表示path 保存当前路径,used[] 表示哪些数被选了
终止条件当 path.Count == nums.Length,加入结果集
剪枝如果 used[i] == true,跳过当前数字
回溯回到上一步时撤销选择(used[i] = false,RemoveAt)

1️⃣ 使用回溯法(Backtracking)

  • 回溯法用于逐步构造解空间,尝试所有可能路径。

  • 每个位置都尝试一个未使用过的数字,直到构成一个完整排列。


2️⃣ 路径构建

  • 使用 List<int> path 表示当前正在构造的排列路径。

  • 使用 bool[] used 数组标记哪些数字已经被使用,避免重复。


3️⃣ 终止条件

  • path.Count == nums.Length 时,说明已经选满一个排列,将其加入结果集。

4️⃣ 遍历选择列表

  • 遍历 nums 中每一个数字:

  • 若未被使用,则加入 path;

  • 递归处理下一层;

  • 回溯时撤销选择(移除 path 最后一个元素,并将 used[i] = false)。


5️⃣ 回溯关键点

  • 做选择 → 递归 → 撤销选择(回溯)

  • 依赖于系统栈自动回溯到上一层状态

public class Solution {public IList<IList<int>> res=new List<IList<int>>();public IList<int> path=new List<int>();public IList<IList<int>> Permute(int[] nums) {var used=new bool[nums.Length] ;BackTracking(nums,used);return res;}public void BackTracking(int[] nums,bool[] used){if(path.Count==nums.Length){res.Add(new List<int>(path));return;}for(int i=0;i<nums.Length;i++){if(used[i]) continue;used[i]=true;path.Add(nums[i]);BackTracking(nums,used);used[i]=false;path.RemoveAt(path.Count-1);}}}

47.全排列 II

思路:

  1. 排序。排序的目的是为了在后面判断重复时能够比较相邻元素,从而避免生成重复排列。

  2. used数组。记录某个元素是否在当前排列中已经被使用,防止重复使用同一个索引的元素。

  3. 剪枝去重条件

if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false) continue;

这句代码是关键的去重操作,意思是:

  • 如果当前数字 nums[i] 和前一个数字 nums[i - 1] 相同,

  • 并且 nums[i - 1] 在当前递归层级没有被使用(即还没进入 path),

  • 那么跳过这个数字,避免重复排列。

public class Solution {public IList<IList<int>> res=new List<IList<int>>();public IList<int> path=new List<int>();public IList<IList<int>> PermuteUnique(int[] nums) {Array.Sort(nums);var used=new bool[nums.Length];BackTracking(nums,used);return res;}public void BackTracking(int[] nums,bool[] used){if(nums.Length==path.Count){res.Add(new List<int>(path));return;}for(int i=0;i<nums.Length;i++){if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false) continue;if(used[i]) continue;path.Add(nums[i]);used[i]=true;BackTracking(nums,used);used[i]=false;path.RemoveAt(path.Count-1);}}
}

回溯总结

  1. 回溯算法能解决如下问题:
  • 组合问题:N个数里面按一定规则找出k个数的集合

  • 排列问题:N个数按一定规则全排列,有几种排列方式

  • 切割问题:一个字符串按一定规则有几种切割方式

  • 子集问题:一个N个数的集合里有多少符合条件的子集

  • 棋盘问题:N皇后,解数独等等

  1. 回溯模板
    void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果}
    }
    
  2. 回溯问题可以抽象为树形结构

  3. for循环横向遍历,递归纵向遍历,回溯不断调整结果集

  4. 剪枝操作:for循环在寻找起点的时候要有一个范围,如果这个起点到集合终止之间的元素已经不够题目要求的k个元素了,就没有必要搜索了。

  5. startIndex:如果是一个集合来求组合的话,就需要startIndex;如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex

  6. 去重:树枝去重/树层去重

http://www.dtcms.com/a/289583.html

相关文章:

  • JAVA面试宝典 -《API设计:RESTful 与 GraphQL 对比实践》
  • Error: You don‘t have permission to access that port.当端口被莫名占用时,kill它!
  • G1回收器垃圾回收过程
  • 30天打牢数模基础-卷积神经网络讲解
  • 将来职业从事股票市场投资工作,应该做什么?
  • Python趣味算法:借书方案知多少 | 排列组合穷举法详解
  • DeepSeek+LoRA微调大模型实战
  • 【机器学习深度学习】魔塔社区模型后缀全解析:Base、Chat、Instruct、Bit、Distill背后的技术密码
  • 【Java多线程-----复习】
  • InfluxDB 核心概念与发展历程全景解读(二)
  • AtCoder Beginner Contest 415
  • 数控调压BUCK电路 —— 基于TPS56637(TI)
  • 乐观锁实现原理笔记
  • Android activity与service通信的三种方法
  • 30天打牢数模基础-K均值聚类
  • (DINO)Emerging Properties in Self-Supervised Vision Transformers论文精读(逐段解析)
  • (苍穹外卖)暑假学习理解P2
  • 从零搭建智能搜索代理:LangGraph + 实时搜索 + PDF导出完整项目实战
  • [C/C++安全编程]_[中级]_[如何安全使用循环语句]
  • k8s:离线部署存在的相关问题
  • 近期工作感想:职业规划篇
  • 【单片机外部中断实验修改动态数码管0-99】2022-5-22
  • Linux文件系统三要素:块划分、分区管理与inode结构解析
  • 本地部署开源离线内容浏览器 Kiwix 并实现外部访问( Windows 版本)
  • 【Java新特性】Java 21 新特性全解析
  • CSS面试题及详细答案140道之(121-140)
  • 快速理解LLM的temperature和top_p参数
  • 设备健康管理实施案例:从技术架构到落地效果的全栈解析
  • MCP实战案例|Trae2.0 一键创建旅行助手并一键部署EdgeOne
  • ARFoundation系列讲解 - 101 VisionPro 真机调试