leetcode 77 组合

题目:

给出n, k。求从1~n里面取k个的不同组合。(不能有重复的)

 

乍一看,这不就和全排列很像吗?不就是= k时记录然后return就好了。但是如果按排列做的话,会有很多重复的。如 [1, 4] 和 [4, 1] 在排列中算不一样的,但在组合中是一样的,和元素出现的顺序无关。所以如果按排列做,会很麻烦,就像我第一次下面这样做的。为了避免超时,各种“优化”。。。(实在是很不推荐)

 1 class Solution {
 2     private List<List<Integer>> ans = new ArrayList<>();
 3     private Set<String> markers = new HashSet<>();
 4     public List<List<Integer>> combine(int n, int k) {
 5         ans.clear();
 6         markers.clear();
 7         if (n <= 0)
 8             return ans;
 9         if (k > n || k < 0)
10             return ans;
11         int[] nums = new int[n];
12         StringBuilder sb = new StringBuilder();
13         for (int i = 0; i < nums.length; i++) {
14             nums[i] = i + 1;
15             sb.append(0);
16         }
17         if (k >= n / 2) {
18             helper(nums, n, sb, n - k, 0, false);
19         }
20         else {
21             helper(nums, n, sb, k, 0, true);
22         }
23         for (String item : markers) {
24             List<Integer> elem = new ArrayList<>();
25             for (int i = 0; i < item.length(); i++) {
26                 if (item.charAt(i) == '1') {
27                     elem.add(i + 1);
28                 }
29             }
30             ans.add(elem);
31         } 
32         return ans;
33     }
34     
35     public void helper(int[] nums, int n, StringBuilder sb, int k, int size, boolean flag) {
36         if (size >= k) {
37             String ts = sb.toString();
38             if (!flag) {
39                 bitReverse(sb);
40                 ts = sb.toString();
41                 bitReverse(sb);
42             }
43             if (!markers.contains(ts))
44                 markers.add(ts);
45             return;
46         }
47         for (int i = 0; i < n; i++) {
48             sb.setCharAt(nums[i] - 1, '1');
49             swap(nums, i, n - 1);
50             helper(nums, n - 1, sb, k, size + 1, flag);
51             swap(nums, i, n - 1);
52             sb.setCharAt(nums[i] - 1, '0');
53         }
54         return;
55     }
56     
57     public void swap(int[] nums, int i, int j) {
58         int tmp = nums[i];
59         nums[i] = nums[j];
60         nums[j] = tmp;
61         return;
62     }
63     
64     public void bitReverse(StringBuilder sb) {
65         for (int i = 0; i < sb.length(); i++) {
66             if (sb.charAt(i) == '0')
67                 sb.setCharAt(i, '1');
68             else
69                 sb.setCharAt(i, '0');
70         }
71     }
72 }
View Code

 

然后看了讨论区的解法,发现其实很简单。

 1 class Solution {
 2     private List<List<Integer>> ans = new ArrayList<>();
 3     
 4     public List<List<Integer>> combine(int n, int k) {
 5         ans.clear();
 6         helper(new ArrayList<Integer>(), n, k, 1);
 7         return ans;
 8     }
 9     
10     public void helper(List<Integer> tmp, int n, int k, int start) {
11         if (k == 0) {
12             ans.add(new ArrayList<Integer>(tmp));
13             return;
14         }
15         
16         //选了某个元素i后,下一次就从i后面的开始选。
17         for (int i = start; i <=  n - (k - 1); i++) {
18             tmp.add(i);
19             helper(tmp, n, k - 1, i + 1);
20             tmp.remove(tmp.size() - 1);
21         }
22     }
23 }

 

关键在于:既然组合是和元素次序无关的,我们不妨就按升序去构造。这样当我们选了某个元素i后,为了避免重复,下一次就从i后面的元素开始选,直到选完k个元素为止。这里还有一个小小的优化,就是可以把for循环中的i <= n改成i <= n - (k - 1)。因为当i > n - (k - 1)时,就算把n - (k - 2)到n的全部元素都选进去,也只有k - 1个,即无法达到k个元素。所以大于的情况可以忽略不计。这样优化后,不论时间复杂度还是空间复杂度,都得到了较大的改善。

转载于:https://www.cnblogs.com/hiyashinsu/p/10714687.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值