一起刷LeetCode——子集(回溯)

147 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情

子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

来源:力扣(LeetCode) 链接:leetcode.cn/problems/su…

分析

枚举

  • 关于数组的子集,首先想到的是枚举,通过遍历数组,遍历第1个元素的时候,把当前元素放到结果中,遍历到第2个元素,除了把当前元素放到结果中,结果中已经存在的子集加上当前元素后生成一个新的自己放到结果中,以此类推,结果是nums数组没有重复的子集
代码
var subsets = function(nums){
    let result = []
    result.push([])
    for(let i=0;i<nums.length;i++){
        let current = nums[i]
        let len = result.length
        for(let j=0;j<len;j++){
            let temp = result[j].slice(0)
            temp.push(current)
            result.push(temp)
        }
    }
    return result
}

递归+回溯

  • 在求子集的时候,数组中的每个元素都有两种状态,被选中在子集中和没有被选中在子集中,在获取某个子集时,以nums的长度为n为例,当前元素下标为i,所以i-1个元素的状态确定的时候,当前i的状态是不确定的,在确定i要不要选中后,当做一个临时的答案放到子集结果中,递归结束后,需要回溯判断临时答案是否是最终结果
  • 回溯基于dfs,和dfs不同的是在搜索过程中,达到条件后,恢复状态回溯到上一次,再次搜索。即满足条件或者发现条件不正确的时候,需要撤销回上一步继续搜索,直到找出所有解
代码
var subset = function(nums){
    let res = []
    const dfs = (i, slate)=>{
        if(i === nums.length){
            res.push(slate.slice())
            return
        }
        slate.push(nums[i])
        dfs(i+1,slate)
        slate.pop()
        dfs(i+1,slate)
    }
    dfs(0,[])
    return res
}

枚举+回溯

  • 因为子集的特殊性,回溯的时候可以与枚举配合,也可以得到最终答案

代码

var subsets = function(nums) {
    const res = []
    
    const backTrack = (index, path = []) => {
        res.push(path)
        for (let i = index; i < nums.length; i++) {
            backTrack(i + 1, [...path, nums[i]])
        }
        return res
    }
    
    return backTrack(0)
};

总结

  • 本题的难度等级为中等,是一个比较典型的回溯算法的例子
  • 当问题的解法需要回头回到上一个状态的时候,可以尝试使用回溯
  • 回溯比较万能的写法是和DFS配合使用,当找到递归的状态树和结束条件后,就可以递归调用以及是否需要剪枝
  • 今天也是有收获的一天