今天是小浩算法 「365刷題計劃」 第105天。這是昨天一個同窗面試快手被問到的算法題,很不幸的是他被掛掉了。徵得對方贊成後,拿出來分享給你們~面試
(若是要進入算法交流羣的,算法
關注後回覆進羣就能夠了)數組
子集:若是集合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
上一個很厲害的題解。
首先咱們能夠證實一下 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 的存儲過程爲:
你們能夠仔細體會一下這個題解。
固然,上面的題解並非凡人能夠直接想到的。因此咱們這裏仍是給出一種更爲通用的題解~
集合中全部元素的選/不選,其實構成了一個滿二叉樹。左子樹選,右子樹不選。天然,那從根節點到全部葉子節點的路徑,就構成了全部的子集。
(旋轉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