递归枚举总结
总结一下递归的几种枚举方法
1.枚举子集:给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集).
递归思路: 对数组里的数依次判断选或者不选,当所有数判断完之后就构成一种结果。
如果数组里有重复元素怎么办?
对同一层中重复的数字取一次就好了,if (i > start && nums[i] == nums[i - 1]) contiue;不要忘了排序.
时间复杂度:2^n * n 一共有2的n次方种状态,每种状态递归n层.
void dfs(int[] nums, int start, ArrayList<Integer> temp, List<List<Integer>> ans) {
ans.add(new ArrayList<>(temp));
for (int i = start; i < nums.length; i++) {
//和上个数字相等就跳过
if (i > start && nums[i] == nums[i - 1]) {
continue;
}
temp.add(nums[i]);
dfs(nums, i + 1, temp, ans);
temp.remove(temp.size() - 1);
}
}
2.组合型枚举:n个元素里选k个
递归思路:和枚举子集的思路相同,加上剪枝判断即可
时间复杂度:n里面选k种的状态数*以k
void dfs(int cur, int n, int k) {
// 剪枝:temp 长度加上区间 [cur, n] 的长度小于 k,不可能构造出长度为 k 的 temp
if (temp.size() + (n - cur + 1) < k) {
return;
}
// 记录合法的答案
if (temp.size() == k) {
ans.add(new ArrayList<Integer>(temp));
return;
}
for(int i=cur;i<=n;i++){
temp.add(i);
dfs(i+1,n,k);
temp.remove(temp.size()-1);
}
}
3.全排列
递归思路:枚举每个位置能存放哪些数.并用标记数组标记
如果数组中有重复数字:加上if(i>0&&nums[i]==nums[i-1]&&!used[i-1])
解释:当出现重复情况时,两个相等的数字nums[i]和nums[i-1]在同一递归层中,此时 used[i-1]一定为false,但其实已经访问过这种情况了,只不过回溯的时候又置为false.
如果used[i-1]为true,那肯定不是在同一递归层中.
时间复杂度:n*n!
void dfs(int cur,int nums[], List<Integer> path,boolean used[]){
if(cur==nums.length){
ans.add(new ArrayList<Integer>(path)); return;
}
for(int i=0;i<nums.length;i++){
if(used[i] ||(i>0&&nums[i]==nums[i-1]&&!used[i-1]) ) continue;
path.add(nums[i]);
used[i]=true;
dfs(cur+1,nums,path,used);
used[i]=false;
path.remove(path.size()-1);
}
}

浙公网安备 33010602011771号