查看: 1839| 回复: 2
跳转到指定楼层
上一主题 下一主题
收起左侧

[高频题] 分享一个subset及其衍生题的模板。这类题,你只需要一个模板

全局:

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看附件。没有帐号?注册账号

x
我们先来看一个经典的78. subset: Given a set of distinct integers, nums, return all possible subsets (the power set).

对于这个题,我们要生成所有的subset,那么我们需要考虑怎么取到所有的可能性,其实思路想明白了很简单:对于每个元素,我们有取和不取两种可能性。借助一个辅助DFS函数,去实现这个取和不取的两种情况,我们就能枚举所有的子集了。代码如下:

  1. class Solution {
  2.     public List<List<Integer>> subsets(int[] nums) {
  3.         List<List<Integer>> res = new ArrayList<>();
  4.         dfs(nums, res, 0, new ArrayList<>());
  5.         return res;
  6.     }
  7.    
  8.     private void dfs(int[] nums, List<List<Integer>> res, int pos, List<Integer> set) {
  9.         if (pos == nums.length) {
  10.             res.add(new ArrayList<>(set));
  11.             return;
  12.         }
  13.         
  14.         
  15.         // pick this element
  16.         set.add(nums[pos]);
  17.         dfs(nums, res, pos + 1, set);
  18.         set.remove(set.size() - 1);
  19.         
  20.         // not pick this element
  21.         dfs(nums, res, pos + 1, set);
  22.     }
  23. }
复制代码


接下来我们看followup, 如果备选元素有重复,怎么生成所有的子集。其实思路也很简单,我们需要判重。所有在上面一题的基础上,加一段判重就可以。其他地方不改变。代码如下:
  1. class Solution {
  2.     public List<List<Integer>> subsetsWithDup(int[] nums) {
  3.         List<List<Integer>> res = new ArrayList<>();
  4.         Arrays.sort(nums);
  5.         dfs(nums, res, 0, new ArrayList<>());
  6.         return res;
  7.     }
  8.    
  9.     private void dfs(int[] nums, List<List<Integer>> res, int pos, List<Integer> set) {
  10.         if (pos == nums.length) {
  11.             res.add(new ArrayList<>(set));
  12.             return;
  13.         }
  14.         
  15.         
  16.         // pick this element
  17.         set.add(nums[pos]);
  18.         dfs(nums, res, pos + 1, set);
  19.         set.remove(set.size() - 1);
  20.         
  21.         // before the not pick element process, we need to get rid of the duplications, if the current element is the same as the next one, just advance the moving pointer
  22.         while (pos + 1 < nums.length && nums[pos + 1] == nums[pos]) {
  23.             pos++;
  24.         }
  25.         
  26.         
  27.         // not pick this element
  28.         dfs(nums, res, pos + 1, set);
  29.     }
  30. }
复制代码


接下来我们来看39. Combination Sum。这个题和subset就是一个题,无非现在我们需要知道subset的和,判断和是不是target就行。如果是,表明找到这个combination。大部分的代码还是和subset一样。代码如下:
  1. class Solution {
  2.     public List<List<Integer>> combinationSum(int[] candidates, int target) {
  3.         List<List<Integer>> res = new ArrayList<>();
  4.         
  5.         dfs(candidates, res, target, 0, new ArrayList<>());
  6.         return res;
  7.         
  8.     }
  9.    
  10.     private void dfs(int[] cands, List<List<Integer>> res, int T, int pos, List<Integer> comb) {
  11.         // if we found the target combination sum
  12.         if (T == 0) {
  13.             res.add(new ArrayList<>(comb));
  14.             return;
  15.         }
  16.         
  17.         // if we undershoot the sum
  18.         if (T < 0) {
  19.             return;
  20.         }
  21.         // if we used all the elements
  22.         if (pos >= cands.length) {
  23.             return;
  24.         }
  25.         // use the current element to make the combination
  26.         comb.add(cands[pos]);
  27.         dfs(cands, res, T - cands[pos], pos, comb);
  28.         comb.remove(comb.size() - 1);
  29.         
  30.         // skip the current element to make the combination by moving the index to next element and keep the T
  31.         
  32.         dfs(cands, res, T, pos + 1, comb);
  33.         
  34.     }
  35. }
复制代码


40. Combination Sum II。这个题和subset II是一样的思路,我们需要判重,那么在上一题的基础上,加上判重逻辑就解决问题。代码如下
  1. class Solution {
  2.     public List<List<Integer>> combinationSum2(int[] candidates, int target) {
  3.         List<List<Integer>> res = new ArrayList<>();
  4.         Arrays.sort(candidates);
  5.         dfs(candidates, target, res, new ArrayList<>(), 0);
  6.         return res;
  7.     }
  8.    
  9.     private void dfs(int[] nums, int T, List<List<Integer>> res, List<Integer> comb, int pos) {
  10.         
  11.         if (T < 0) return;
  12.         if (T == 0) {
  13.             res.add(new ArrayList<>(comb));
  14.             return;
  15.         }
  16.         if (pos >= nums.length) return;
  17.         
  18.         comb.add(nums[pos]);
  19.         dfs(nums, T - nums[pos], res, comb, pos + 1);
  20.         comb.remove(comb.size() - 1);
  21.         
  22.         
  23.         // deduplicate, the other parts are just like Q.39.
  24.         while (pos + 1 < nums.length && nums[pos + 1] == nums[pos]) { // this place is error prone. WHile instead of if
  25.             pos++;
  26.         }
  27.         
  28.         //if (pos + 1 < nums.length && nums[pos + 1] == nums[pos]) continue;
  29.         
  30.         dfs(nums, T, res, comb, pos + 1);
  31.         
  32.     }
  33. }
复制代码


77. Combinations. 这个题还是沿用一样的subset的模板。无非现在是取值的范围变化了而已。其他的逻辑还是一样的。代码如下:
  1. class Solution {
  2.     public List<List<Integer>> combine(int n, int k) {
  3.         List<List<Integer>> res = new ArrayList<>();
  4.         dfs(1, n, k, res, new ArrayList<>());
  5.         return res;
  6.     }
  7.    
  8.     private void dfs(int s, int e, int count, List<List<Integer>> res, List<Integer> comb) {

  9.         
  10.         if (comb.size() == count) {
  11.             res.add(new ArrayList<>(comb));
  12.             return;
  13.         }
  14.         
  15.         
  16.         if (s >= e + 1) {
  17.             return;
  18.         }
  19.         
  20.         comb.add(s);
  21.         dfs(s + 1, e, count, res, comb);
  22.         comb.remove(comb.size() - 1);

  23.         dfs(s + 1, e, count, res, comb);
  24.         
  25.     }
  26. }
复制代码


216. Combination Sum III。这个题还是一样的模板题。根据题意改一下base case就行。代码如下:
  1. class Solution {
  2.     public List<List<Integer>> combinationSum3(int k, int n) {
  3.         List<List<Integer>> res = new ArrayList<>();
  4.         dfs(k, n, res, 1, new ArrayList<>());
  5.         return res;
  6.     }
  7.    
  8.     private void dfs(int k, int n, List<List<Integer>> res, int s, List<Integer> comb) {

  9.         if (n < 0) {
  10.             return;
  11.         }
  12.         if (n == 0 && comb.size() == k) {
  13.             res.add(new ArrayList<>(comb));
  14.             return;
  15.         }
  16.         
  17.         if (s >= 10) {
  18.             return;
  19.         }
  20.         
  21.         comb.add(s);
  22.         dfs(k, n - s, res, s + 1, comb);
  23.         comb.remove(comb.size() - 1);
  24.         
  25.         dfs(k, n, res, s + 1, comb);
  26.         
  27.         
  28.     }
  29. }
复制代码


总结:通过subset每个元素还是不取,我们就能枚举一个数组里面所以的子集。然后根据题意去构造最终的目标。这六个题就迎刃而解了。而且这样写起来,出错的可能性更低。

Happy coding。新手上路,发点有营养的帖子,希望各位看官走过路过加点米,好看面经,刷题上岸。先谢过了。




评分

参与人数 5大米 +17 收起 理由
俘虏你的心 + 3 很有用的信息!
14417335 + 10
joestar + 1 给你点个赞!
jesse1204 + 2 很有用的信息!
fenn + 1 给你点个赞!

查看全部评分


上一篇:决定转码一个多月以来
下一篇:小白提问 感觉LeetCode提升不够

本帖被以下淘专辑推荐:

全局:
感谢楼主分享,补充一个permutation的题, 好像是131 palindrome permutation。 然后中国面试有一个特别爱问的是 字符串的全排列。 俩题基本和楼主的题是一个模子里的

我的个人感觉是 subset, permutation的本质区别在于 画完递归树,subset是路径上节点都会往里加, 然后permutation只会去加叶子节点。 然后基于此,有一些比如去重/排序等小trick来帮助剪枝,还有一些小小的变形题,可能会对加入res有一些额外的要求。

好久没做这块儿的题里,纯凭记忆打的,不一定对

评分

参与人数 1大米 +1 收起 理由
dontbeevil + 1 很有用的信息!

查看全部评分

回复

使用道具 举报

🔗
ggaimm2002 2019-10-7 23:12:58 | 只看该作者
全局:
楼主总结的很好
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表