Day25| 491.递增子序列、46.全排列
文章链接
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);}}}