又一個同窗被快手掛掉了

又一個同窗被快手掛掉了


今天是小浩算法 「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

相關文章
相關標籤/搜索