- 數組中的元素不重複同時數組中的元素
可以重複取
,這種從一個集合中挑選元素的方法來滿足某種條件的問題。一般可以選用遞歸+回溯
,選擇了一個條件並在這個條件下走下去,若發現滿足題目的要求,則這條路徑可以作爲一種可能
,若走不下去則不能。這條路徑走完了,接着走另外的一條路
;同時可以進行剪枝,`當前的情況下一定不滿足最終的結果,不必再走了;搜索問題一般複雜度較高,能剪枝就儘量剪枝,把候選數組拍個序,遇到一個較大的數,如果以這個數位起點都搜索不到結果,後面的數就更搜索不到結果。
package BDyNamicProgramming;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/4/25 0025 13:25
*/
public class Problem39 {
//求組合的和爲target的所有可能
public List<List<Integer>> combinationSum(int[] candidates, int target){
List<List<Integer>> rs = new ArrayList<>();
List<Integer> r = new ArrayList<>();
//排序是爲了提前終止搜索
Arrays.sort(candidates);
//組合中的數可以重複的取
fun(candidates,target,rs,r,0);
return rs;
}
//深度優先搜索:
//這種索索是全排列不是全組合
//不加start會存在問題
/*
[[2, 2, 3], [2, 3, 2], [3, 2, 2], [7]],而示例中的解集只有 [[7], [2, 2, 3]]
重複的原因是在較深層的節點值考慮了之前考慮過的元素,因此我們需要設置“下一輪搜索的起點"即可
去重複:
在搜索的時候,需要設置搜索起點的start,由於一個數可以使用多次,下一層的節點從這個搜索起點開始搜索
在搜索起點之前start之前,因爲以前的分支搜索過了,所以一定會產生重複
提前剪樹枝:
如果一個數位搜索起點都不能搜索到結果,那麼比他還大的數肯定搜索不到結果,基於這個想法,我們可以對輸入數組進行排序,以減少搜索分支
排序是爲了提高搜索速度,非必要
搜索問題一般複雜度較高,能剪值就儘量剪值,把候選數組排個序,遇到一個較大的數,如果把這個歌樹爲起點都搜索不到結果,後面的數就更搜索不到
*/
/**
*
* @param candidates 數組輸入
* @param target 剩餘數值
* @param rs 結果集變量
* @param r 從根接地那到任意節點的路徑
* @param start 本輪搜索的起點下標(這個很關鍵)
*/
public void fun(int[] candidates,int target,List<List<Integer>> rs,List<Integer> r,int start){
//符合值相等
if(target==0){
//由於r全局只適用到一份,到葉子節點的時候需要做一份拷貝
rs.add(new ArrayList<>(r));
}
if(target<0) return;
//遞歸+回溯
for(int i=start;i<candidates.length;i++){
//在數組有序的前提下剪值
if(target-candidates[i]<0) break;
//將當前值加入到list中
r.add(candidates[i]);
//組合中的數可以重複的取值,i爲下輪搜索的起始點
fun(candidates,target-candidates[i],rs,r,i);
//進行回溯
r.remove((Object)candidates[i]);
}
}
public static void main(String[] args) {
int[] arr = {2,3,6,7};
int target = 7;
Problem39 problem39 = new Problem39();
List<List<Integer>> rs = problem39.combinationSum(arr,target);
System.out.println(rs);
}
}