题目描述:
找出所有相加之和为 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;
}