递归枚举总结

总结一下递归的几种枚举方法

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);
        }   
    }    
posted @ 2021-02-04 16:08  大塞翁  阅读(275)  评论(0)    收藏  举报