力扣216题 组合总和

题目描述:
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]

方法一:二进制数表示法

注意这句话:
组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

也就是说我们其实可以列举出所有可能。有没有包含某个数字为一种可能,我们可以用9个二进制来表示,第一个二进制数0表示这个组合中没有1.如果为1则表示组合中含有1.
第二个二进制数就表示有没有包含2,以此类推,9个二进制数就可以包含所有情况。

而我们在用一定的方法去获取这个九位二进制数中的1.然后对应道这个组合含有哪个数上

import org.omg.PortableInterceptor.INACTIVE;

import java.util.ArrayList;
import java.util.List;

public class num1 {

    List<List<Integer>>list2=new ArrayList<>();

    List<List<Integer>>method(int k,int n){

        List<Integer>list1=new ArrayList<>();
        for(int mask=0;mask<(1 << 9);++mask)//左移运算符,得到000000001到1000000000的所有数
        {
           if(check(k,n,mask,list1))     
                         list2.add(new ArrayList<>(list1));//因为下面还会对list1进行操作,所以尽量这里添加的时候要新建一个Arraylist把list1传进去
            //这样对原本的list1不会造成太大影响
            //如果单纯只添加list1的话,后续对list1操纵可能会造成影响,因为被添加到里面了,会造成什么后果也不为人知
        }
        return list2;
    }

    boolean check(int k,int n,int mask,List<Integer>list1)
    {
        list1.clear();
        for(int i=0;i<9;i++)
        {
            if(((1<<i) & mask)!=0)//这样可以得到这个9位二进制数哪位为1,哪位为1,就相余不为0,记录下来
                list1.add(i+1);
        }
        if(list1.size()!=k)
        {return false;}

        int sum=0;
        for(int num:list1)
        {
            sum=sum+num;
        }
      return sum==n;
    }

    public static void main(String[] args) {
        num1 num11=new num1();
        System.out.println(num11.method(3,8));
    }

}

方法2:(回溯算法。组合问题)

讲这个方法之前先看另一道题:

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

在这里插入图片描述
这道题就是典型的回溯算法问题,回溯算法的例题也就是这道范围内组合的问题。

这道题(回溯)的本质就是递归加for循环。

代码如下:

import java.util.*;

public class num2 {

    List<List<Integer>> method(int n,int k)
    {
        List<List<Integer>> list1=new ArrayList<>();
        Deque<Integer>list2=new ArrayDeque<>();
        backtraing(n,k,1,list1,list2);
        return list1;

    }

void backtraing(int n,int k,int startindex,List<List<Integer>> list1,Deque<Integer>list2)
{
    if(list2.size()==k) {
        list1.add(new ArrayList<>(list2));
        return;
    }

for(int i=startindex;i<n;i++)
{
    list2.add(i);
    backtraing(n,k,i+1,list1,list2);
    list2.removeLast();//这是回溯算法最重要的,上面的递归结束,就是一个for的数的分支结束,就得弹出,去走另一条路。
}

}
    public static void main(String[] args) {
        num2 num22=new num2();
        System.out.println(num22.method(10, 3));
    }
}

当然还有一个剪枝版本,只要把for循环里的n换成 n-(k-list1.size())+1.(详情可以看力扣的第二个题解),讲得挺清楚的。

接下来就讲讲,用这个方法解决第一道题。

就比如

输入: k = 3, n = 7
输出: [[1,2,4]]

我们直接找出1到7 由3个数构成的组合的所有集合,然后for循环,将其中成立的存储下来。

把前面改动如下:

    List<List<Integer>> method(int n,int k)
    {
        List<List<Integer>> list1=new ArrayList<>();
        Deque<Integer>list2=new ArrayDeque<>();
        backtraing(n,k,1,list1,list2);
        List<List<Integer>> list3=new ArrayList<>();
        for(List<Integer>aa:list1)
        {
            if(check(aa,n)) list3.add(aa);
        }
        return list3;
    }

    boolean check(List<Integer>list,int n)
    {
        int sum=0;
        for(int num:list)
        {
            sum=sum+num;
        }
         return sum==n;

    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值