回溯算法:子集问题

90 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 13 天,点击查看活动详情

子集问题包括有重复元素和无重复元素两种,比如leetcode上面的78、90两题。

image.png

这里的重复元素是指所给数组是否有重复元素。

我们就按78、79两题来介绍回溯算法,如上图。子集问题返回的是集合,我们需要先定义一个返回二维集合,用来存放我们的结果集。然后再定义一维的集合(结果集)来存放我们的结果。

上面这句话可能有点绕口,对应图上来说会好理解一些:如图中[1,2,3]就是我们的结果集。

定义好之后就可以开始想我们的回溯算法了。下面采用“回溯三部曲”进行分析:

不要在意有没有回溯三部曲,自己瞎起的。

首先思考回溯的结束条件:当结果集的大小等于数组的大小时,就是遍历完了,要返回了。

    if(path.size() == nums.length) {
        res.add(new ArrayList(path));
        return;
    }

从nums数组下标0开始遍历数组,遍历到一个元素我们就将其放到结果集中。然后对数组剩余元素进行递归。就比如我们将1放入结果集后,又进入了回溯函数(先将结果集[1]放到二维集合中),但会从下标+1开始遍历,这样我们就将2放到了结果集(同样会将[1,2]放到二维集合中),当把3放入结果集后,发现满足结束条件了,就将这次结果集弹出来了,结果集删除末尾元素以还原到上一步。那具体代码如下:

class Solution {
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        backtracking(nums, 0);
        return res;
    }

    public void backtracking(int[] nums, int startIndex){
        res.add(new ArrayList(path));

        if(path.size() == nums.length) return;

        for(int i=startIndex; i<nums.length; i++){
            path.add(nums[i]);
            backtracking(nums, i+1);
            path.remove(path.size()-1);
        }
    }
}

对于含重复元素类型的自己问题,最后只是多了个去重,思路是一样的。我们在将结果集放入二维集合的时候去判断一下二维集合中是否已经有相同结果集了就ok的。

    if(!res.contains(tmp)){
        res.add(new ArrayList<>(tmp));
    }

注意:面对有重复元素的问题,最好是先将数组做个排序。

以上是我个人对于回溯中子集问题的理解。若对jym理解回溯算法有帮助的话可以点个大拇指👍鼓励鼓励...