找出所有相加之和為 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×(n−1)!
回溯是一種算法思想,它是用遞歸實現的。回溯的過程類似於窮舉法,但回溯有“剪枝”功能,即自我判斷過程。例如有求和問題,給定有 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
