前端刷题路-Day75:子集(题号78)

271 阅读1分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

子集(题号78)

题目

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

链接

leetcode-cn.com/problems/su…

解释

这题啊,这题是经典排列组合。

笔者一开始有点陷入思维误区了,一直尝试着去找各种可能,其实这样做是没有结果的。

果然,在20分钟后,笔者放弃了这种思路。

之后笔者发现可以对数组进行拆解,比方说一个数组的长度为4,那么是否可以逐渐增加数组的长度,首先数组的长度为0,之后逐渐往里面增加数字,每增加一个数字将遍历一下之前的结果,在每个结果后面都添加上当前数字,如此每次都让最后的结果翻一倍,也符合整体的递增规律,也就是2的N次方。

到这里基本上答案就出来了,👇看看代码。

自己的答案(双循环)

function subsets(nums) {
  const res = [[]]
  for (const num of nums) {
    const len = res.length
    for (let i = 0; i < len; i++) {
      res.push(res[i].concat(num))      
    }
  }
  return res
}

双层循环还是很好操作的,每次添加新数组到res中时切记不要修改源数组,这样可能会导致元素重复的问题,不过使用这种方法大概率不会出现这种问题。

更好的方法(回溯+递归)

着不能说是更好的方法,只能说官方提供的另外一直解法吧,性能上这两种方案都差不多,时间和内存都在90%以上。

回溯需要考虑的就是当前元素是否被选中,怎么理解这个被选中呢?

比方说现在有4个数字,分别是1、2、3、4。那么如果要进行排列组合,先考虑一个数字的情况,完全可以只选择一个数字而不考虑其它的数字,如果是选择两个数字的情况,那就得挑着选择数字了,可以先选2试试,然后走到3个数字的情况和4个数字的情况,挑选数字等到最后的结果后,将当前挑选的数字插入结果数组,然后撤回刚才挑选的数字,继续走下一种情况。

说起来可能比较模糊,看看代码会清晰很多👇:

function subsets(nums) {
  const len = nums.length
  const res = []
  const target = []
  function DFS(index) {
    if (index === len) {
      return res.push(target.slice(0))
    }
    target.push(nums[index])
    DFS(index + 1)
    target.pop(nums[index])
    DFS(index + 1)
  }  
  DFS(0)
  return res
}

搞一个target数组,用来存放挑选的数字,完事了进行一个递归操作,先是终止条件,如果选到头了就将结果复制一份推入数组;如果没到头,就选一下当前的数字,开始下一轮递归,然后再去掉当前数字,也就是不选当前数字,开始下一轮递归。

简单来说每个数字都会有选和不选两种情况,那么此时就会覆盖到所有可能出现的情况,打完收工。



PS:想查看往期文章和题目可以点击下面的链接:

这里是按照日期分类的👇

前端刷题路-目录(日期分类)

经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的👇

前端刷题路-目录(题型分类)