找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。(回溯思想)

找出所有相加之和为 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 class Solution {
 2     public List<List<Integer>> combinationSum3(int k, int n) {
 3         List<List<Integer>> result = new ArrayList<>();
 4         for(int i=0;i < result.size();i++){
 5             List<Integer> list = new ArrayList<>();
 6             List<Integer> re = digui(i,list,n,k);
 7             if(re !=null){
 8                 result.add(re);
 9             }
10         }
11         return result;
12     }
13 
14     List<Integer> digui(int c,List<Integer> a,int n,int k){
15         int b;
16         if(a.size() == 0){
17             b = c;
18             a.add(b);
19             return digui(c,a,n,k);
20         }else{
21             b = a.get(a.size()-1)+1;
22         }
23         
24         int total = 0;
25         for(int aa:a){
26             total +=aa;
27         }
28         if(total == n){
29             return a;
30         }
31         if(b>9 || total > n ||a.size()>k)
32             return null;
33         a.add(b);
34         return digui(c,a,n,k);
35     }
36 }

 递归不行,要使用回溯进行解决

 1 class Solution {
 2     //结果集
 3     public static List<List<Integer>> lists = new ArrayList<List<Integer>>();
 4     //临时集
 5     public static List<Integer> list = new ArrayList<Integer>();
 6     public List<List<Integer>> combinationSum3(int k, int n) {
 7         huisu(1,list,n,k,0,0);
 8         return lists;
 9     }
10 
11     void huisu(int c,List<Integer> a,int n,int k,int sum,int index){
12         if(k>n){
13             return;
14         }
15         if(index == k && sum == n){
16             List<Integer> currentList = new ArrayList<Integer>();
17             currentList.addAll(list);
18             lists.add(currentList);
19             return;
20         }
21 
22         if(sum > n){
23             return;
24         }
25         for(int i = c;i<= 9; ++i){
26             list.add(i);
27             sum+=i;
28             huisu(i+1,list,n,k,sum,index+1);
29             list.remove(list.size()-1);
30             sum -=i;
31         }
32     }
33 }

递归和回溯的区别

递归:程序调用自身的编程技巧。

  作为一种程序设计算法,有着广泛应用。需要注意的是,递归结束的条件要控制好。

回溯:是一种算法思想,可以用递归来实现。

  回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就"回溯"返回,尝试别的路径。

  回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。

回溯思路:

  基本如下:当前局面下,我们有若干种选择,所以我们对每一种选择进行尝试。如果发现某种选择违反了某些限定条件,此时 return;如果尝试某种选择到了最后,发现该选择是正确解,那么就将其加入到解集中。
  在这种思想下,我们需要清晰的找出三个要素:选择 (Options),限制 (Restraints),结束条件 (Termination)。

回溯算法框架:

 

 1 result = []
 2 def backtrack(路径, 选择列表):
 3     if 满足结束条件:
 4         result.add(路径)
 5         return
 6     
 7     for 选择 in 选择列表:
 8         做选择
 9         backtrack(路径, 选择列表)
10         撤销选择

其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」,特别简单。

递归与回溯的区别

递归是一种算法结构。递归会出现在子程序中,形式上表现为直接或间接的自己调用自己。典型的例子是阶乘,计算规律为:n!=n×(n1)!

回溯是一种算法思想,它是用递归实现的。回溯的过程类似于穷举法,但回溯有“剪枝”功能,即自我判断过程。例如有求和问题,给定有 7 个元素的组合 [1, 2, 3, 4, 5, 6, 7],求加和为 7 的子集。累加计算中,选择 1+2+3+4 时,判断得到结果为 10 大于 7,那么后面的 5, 6, 7 就没有必要计算了。这种方法属于搜索过程中的优化,即“剪枝”功能。

 

用一个比较通俗的说法来解释递归和回溯:
我们在路上走着,前面是一个多岔路口,因为我们并不知道应该走哪条路,所以我们需要尝试。尝试的过程就是一个函数。
我们选择了一个方向,后来发现又有一个多岔路口,这时候又需要进行一次选择。所以我们需要在上一次尝试结果的基础上,再做一次尝试,即在函数内部再调用一次函数,这就是递归的过程。
这样重复了若干次之后,发现这次选择的这条路走不通,这时候我们知道我们上一个路口选错了,所以我们要回到上一个路口重新选择其他路,这就是回溯的思想

 

 

参考:https://www.cnblogs.com/fanguangdexiaoyuer/p/11224426.html

   https://blog.csdn.net/summer_dew/article/details/8392158

      https://blog.csdn.net/a1439775520/article/details/104539875

posted @ 2020-09-11 17:43  Jeessu  阅读(1862)  评论(0编辑  收藏  举报