找出所有相加之和為 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM