又一个同窗被快手挂掉了

又一个同窗被快手挂掉了


今天是小浩算法 “365刷题计划” 第105天。这是昨天一个同窗面试快手被问到的算法题,很不幸的是他被挂掉了。征得对方赞成后,拿出来分享给你们~面试

又一个同窗被快手挂掉了

(若是要进入算法交流群的,算法

关注后回复进群就能够了)数组

又一个同窗被快手挂掉了

01

PART

子集


子集:若是集合A的任意一个元素都是集合B的元素,那么集合A称为集合B的子集。ide


第48题:给定一组不含重复元素的整数数组 nums,返回该数组全部可能的子集(幂集)。3d


说明:解集不能包含重复的子集code

示例:blog

输入: nums = [1,2,3] 递归

输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ] rem


题目自己没有太多须要补充的,初中数学知识:get

又一个同窗被快手挂掉了

02

PART

题解分析(吊)


上一个很厉害的题解。


首先咱们能够证实一下 N 个元素的子集个数有 2^N 个:

又一个同窗被快手挂掉了

能够类比为 N 个不一样的小球,一次拿出若干个小球(能够不拿),对于每个球均可以选择拿或者不拿,共有 N 个球,总共判断 N 次,产生了 2^N 个子集。好比:123,共有下面 8 个子集:

又一个同窗被快手挂掉了

而后考虑解题思路,暂且不谈回溯,咱们其实能够用二进制来模拟每一个元素是否选中的状态。又由于咱们已知了对于 N 个元素共有 2^N 个子集,因此咱们直接遍历 2^N 个元素。

1class Solution {
 2    public List<List<Integer>> subsets(int[] nums) {
 3        //存放全部子集
 4        List<List<Integer>> res = new ArrayList<>();
 5        //子集总数共有 2^N 个
 6        int length = 1 << nums.length;
 7        //遍历全部的子集
 8        for (int i = 0; i < length; i++) {
 9            List<Integer> sub = new ArrayList<>();
10            //TODO : 找到对应的子集元素
11        }
12        return res;
13    }
14}

可是咱们并不知道具体的子集元素。那如何找到对应的子集元素呢?对于 2^N 个 N 位的二进制数,咱们能够经过从后往前的第 j 个二进制位的 0 和 1 来表示是否放入子集集合

1for (int j = 0; j < nums.length; j++) {
2     if (((i >> j) & 1) == 1) sub.add(nums[j]);
3}

综合一下代码:

1class Solution {
 2    public List<List<Integer>> subsets(int[] nums) {
 3        //存放全部子集
 4        List<List<Integer>> res = new ArrayList<>();
 5        //子集总数公有 2^N 个
 6        int length = 1 << nums.length;
 7        //遍历全部的子集
 8        for (int i = 0; i < length; i++) {
 9            List<Integer> sub = new ArrayList<>();
10            for (int j = 0; j < nums.length; j++) {
11                if (((i >> j) & 1) == 1) sub.add(nums[j]);
12            }
13            res.add(sub);
14        }
15        return res;
16    }
17}

为帮助你们理解,假设 nums 为 [1,2,3],res 的存储过程为:

又一个同窗被快手挂掉了

你们能够仔细体会一下这个题解。

又一个同窗被快手挂掉了

03

PART

题解分析(普通)


固然,上面的题解并非凡人能够直接想到的。因此咱们这里仍是给出一种更为通用的题解~


集合中全部元素的选/不选,其实构成了一个满二叉树。左子树选,右子树不选。天然,那从根节点到全部叶子节点的路径,就构成了全部的子集。

又一个同窗被快手挂掉了

(旋转90°)

又一个同窗被快手挂掉了

那这种解法其实就好理解不少了:

1class Solution {
 2
 3    List<List<Integer>> res;
 4
 5    public List<List<Integer>> subsets(int[] nums) {
 6        res = new ArrayList<>();
 7        List<Integer> list = new ArrayList<>();
 8        dfs(nums, 0, list);
 9        return res;
10    }
11
12    private void dfs(int[] nums, int start, List<Integer> list) {
13        for (int i = start; i < nums.length; i++) {
14            list.add(nums[i]);
15            dfs(nums, i + 1, list);
16            list.remove(list.size() - 1);
17        }
18        res.add(new ArrayList<>(list));
19    }
20
21}

若是对这种解法也不理解,能够看下我以前的二叉树系列:

万字长文!二叉树入门和刷题看这篇就够了!

总之,这道题目其实仍是有必定难度的,难点主要包括:

  • 数学知识的混淆,忘记考虑空集等状况。

  • 和全排列问题混淆,把 2^N 当作 N!处理。

  • 递归与回溯细节掌握不扎实。

但并非不能够攻克,建议你们下去自行练习一番~

加油,奥利给!


若是你也想加入咱们每日刷题

扫码,回复【进群】就能够啦。

推荐几篇必看文章:

漫画:小白为了面试如何刷题?(呕心沥血算法指导篇)

漫画:呕心泣血算法指导篇(真正的干货,怒怼那些说算法没用的人)

动态规划入门看这篇就够了,万字长文!

万字长文!位运算面试看这篇就够了!

彩蛋:

1//py2class Solution:3 def subsets(self, nums: List[int]) -> List[List[int]]:4 arr = [[]]5 for i in nums:6 arr+=[j+[i] for j in arr]7 return arr

相关文章
相关标签/搜索