組み合わせ問題: 特定の規則に従って、N 個の数字から k 個の数字のセットを見つける. 以下は、組み合わせ問題を解決するための一般的なバックトラッキング アルゴリズムのトピックです。
leedcode77.コンビネーション
2 つの整数 n と k を指定すると、範囲 [1, n] 内の k 個の数値のすべての可能な組み合わせを返します。
回答は任意の順序で返すことができます。
バックトラッキング メソッドがツリー構造に抽象化された後のトラバーサル プロセスは、for ループの水平トラバーサル、再帰的な垂直トラバーサル、および結果セットを継続的に調整するためのバックトラッキングです。
class Solution {
//回溯算法
public List<List<Integer>> combine(int n, int k) {
//List<Integer> path=new ArrayList<>();
Deque<Integer> path=new LinkedList<>();
List<List<Integer>> res=new ArrayList<>();
dfs(n,k,1,path,res);
return res;
}
public void dfs(int n,int k,int startIndex, Deque<Integer> path,List<List<Integer>> res){
if(path.size()==k){
//终止条件
res.add(new ArrayList<>(path));
return;
}
//控制树的横向遍历
for(int i=startIndex;i<=n-(k-path.size())+1;i++){
//剪枝处理
path.add(i);
dfs(n,k,i+1,path,res);
path.removeLast();//回溯,弹出处理的节点
}
}
}
leedcode216 合計合計 III
合計が n である k 個の数のすべての組み合わせを見つけ、次の条件を満たします。
1 ~ 9 の数字のみを使用します。
各数字は最大 1 回使用します。
考えられるすべての有効な組み合わせのリストを返します。リストに同じ組み合わせを 2 回含めることはできず、組み合わせは任意の順序で返される可能性があります。
例 1:
入力: k = 3, n = 7 出力: [[1,2,4]] 説明: 1 + 2 + 4 = 7 他に一致する組み合わせはありません。
例 2:
入力: k = 3, n = 9 出力: [[1,2,6], [1,3,5], [2,3,4]] 説明: 1 + 2 + 6 = 9 1 + 3 +
5 = 9 2 + 3 + 4 = 9 他に一致する組み合わせはありません。
例 3:
入力: k = 4、n = 1 出力: [] 説明: 有効な組み合わせが存在しません。
範囲 [1,9] の 4 つの異なる数値を使用すると、得られる最小の合計は 1+2+3+4 = 10 になります。10 > 1 であるため、有効な組み合わせはありません。
class Solution {
List<List<Integer>> result=new ArrayList<>();
Deque<Integer> path=new LinkedList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
dfs(k,n,1,0);
return result;
}
public void dfs(int k,int n,int startIndex,int sum){
//剪枝
if(sum>n){
return;
}
if(k==path.size()){
if(sum==n){
result.add(new ArrayList<>(path));
return;
}
}
for(int i=startIndex;i<=9-(k-path.size())+1;i++){
path.add(i);
sum=sum+i;
dfs(k,n,i+1,sum);
path.removeLast();
sum=sum-i;
}
}
}
leedcode46. 完全順列
数値が繰り返されていない配列 nums を指定すると、考えられるすべての順列を返します。回答は任意の順序で返すことができます。
例 1:
入力: nums = [1,2,3] 出力: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1 ,2],[3,2,1]]
例 2:
入力: nums = [0,1] 出力: [[0,1],[1,0]]
例 3:
入力: 数値 = [1] 出力: [[1]]
leedcode17. 電話番号のアルファベットの組み合わせ
2 ~ 9 の数字のみを含む文字列を指定すると、それが表現できるすべての文字の組み合わせを返します。回答は任意の順序で返すことができます。
数字から文字へのマッピングは次のように与えられます (電話キーと同じ)。1 はどの文字にも対応しないことに注意してください。
例 1:
入力: 数字 = "23"
出力: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
例 2:
入力: 数字=""
出力: []
例 3:
入力: 数字 = "2"
出力: ["a", "b", "c"]
組み合わせを探す集合の場合はstartIndexが必要です。
複数のコレクションが結合され、各コレクションが互いに影響しない場合、startIndex は使用されません。
この質問では、複数のコレクションを組み合わせているため、startIndex を使用せず、毎回 i=0 から開始します。
class Solution {
private List<String> res= new ArrayList<>();
public List<String> letterCombinations(String digits) {
if(digits.length()==0||digits==null){
return res;
}
//0和1无效,2到9有效
String[] s={
"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
//res = new ArrayList<>();
isBackTest(s,digits,0);
return res;
}
//涉及字符串拼接可以使用StringBuilder
StringBuilder temp=new StringBuilder();
//三个参数分别是字符串s,要组合的字符digits,和组合的个数num
private void isBackTest(String[] s, String digits, int num) {
if(num==digits.length()){
//如果要组合的字符长度等于组合个数直接添加并返回
// System.out.println("进来");
res.add(temp.toString());
return;
}
//System.out.println("再进来");
String str=s[digits.charAt(num)-'0'];//比如digits="23",我们可以获取'2'-'0'=2,'3'-'0'=3
for(int i=0;i<str.length();i++){
temp.append(str.charAt(i));//添加字符串中的字符
isBackTest(s,digits,num+1);//递归
temp.deleteCharAt(temp.length()-1);//回溯,减去末尾的元素,此时的num是当前方法的num
}
}
}
leedcode39. 合計
要素が繰り返されない整数配列候補とターゲット整数ターゲットが与えられた場合、数の合計ターゲット数ターゲットを作成できる候補のさまざまな組み合わせをすべて見つけて、リストの形式で返します。これらの組み合わせを任意の順序で返すことができます。
候補は同じ数を無制限に何度でも選択できます。少なくとも 1 つの数字の選択された数字が異なる場合、2 つの組み合わせは異なります。
特定の入力に対して、合計がターゲットになるさまざまな組み合わせの数は、150 未満であることが保証されています。
例 1:
入力: 候補 = [2,3,6,7]、ターゲット = 7 出力: [[2,2,3],[7]]
説明: 2 と 3 は候補のセット、2 + 2 + 3 = を形成できます。 7 . 注 2 は複数回使用できます。7 も候補で、 7 = 7 です。この2つの組み合わせしかありません。
例 2:
入力: 候補 = [2,3,5]、ターゲット = 8
出力: [[2,2,2,2]、[2,3,3]、[3,5]]
例 3:
入力: 候補 = [2]、ターゲット = 1
出力: []
この質問と 77. コンビネーション 216. 前に説明したコンビネーション サム III との間には 2 つの違いがあります。
- 組み合わせに数量要件はありません
- 要素は無限に選択できます
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
//先排好序后面如果有大于target可以直接返回
Arrays.sort(candidates);
//相比与组合III,这道题增加了同一个元素可以被无限重复选取,还有就是递归结束条件不是第几层而是大于等于target
isBackTest(candidates,target,0,0);
return res;
}
List<List<Integer>> res=new ArrayList<>();
List<Integer> path=new ArrayList<>();
private void isBackTest(int[] candidates, int target, int startIndex,int sum) {
if(sum>target){
return;
}
if(sum==target){
res.add(new ArrayList<>(path));
return;
}
//剪枝条件不符合sum+candidates[i]<=target,连递归都不需要调
for(int i=startIndex;i<candidates.length&&sum+candidates[i]<=target;i++){
sum=sum+candidates[i];
path.add(candidates[i]);
isBackTest(candidates,target,i,sum);//这里不用i+1,表示可以重复读取当前的数
path.remove(path.size()-1);//回溯
sum=sum-candidates[i];
}
}
}
leedcode40. 合計 II
候補数候補の集合と目標数目標が与えられたとき、数の和を目標数にすることができる候補のすべての組み合わせを見つけます。
候補の各数字は、各組み合わせで 1 回だけ使用できます。
注: ソリューション セットに重複した組み合わせを含めることはできません。
例 1:
入力: 候補 = [10,1,2,7,6,1,5]、ターゲット = 8、
出力: [ [1,1,6]、[1,2,5]、[1,7]、[ 2,6] ]
例 2:
入力: 候補 = [2,5,2,1,2]、ターゲット = 5、
出力: [ [1,2,2]、[5] ]
この質問と 39 の違いは次のとおりです。組み合わせた合計は次のとおりです。
この質問の候補の各数字は、各組み合わせで 1 回しか使用できません。
この質問の配列候補の要素は繰り返され、39. の合計の組み合わせは、繰り返される要素のない候補の配列です.
最後に、この質問は 39. と同じです. 組み合わせの合計の要件は同じです.ソリューション セットに繰り返しの組み合わせを含めることはできません。
この質問の難しさは、コレクション (配列候補) に要素が繰り返されていることですが、組み合わせを繰り返すことはできません。
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
//先进行排序
Arrays.sort(candidates);
//新建一个used数组,用来进行标记和去重
boolean[] used = new boolean[candidates.length];
isBackTest(candidates, target, 0, 0, used);
return result;
}
List<List<Integer>> result=new ArrayList<>();
List<Integer> path=new ArrayList<>();
private void isBackTest(int[] candidates, int target, int sum, int statIndex, boolean[] used) {
// if(sum>target){//已经在for循环的之后进行剪枝了,不会进入递归也不用写这条语句
// return;
// }
if(sum==target){
result.add(new ArrayList<>(path));
return;
}
for(int i=statIndex;i<candidates.length&&candidates[i]+sum<=target;i++){
if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false){
//去重
//递归回溯过程可以看成树结构
//used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
//used[i - 1] == false,说明同一树层candidates[i - 1]使用过
//如果当前一个元素和上一个元素(相邻)相同,并且上一个元素的used[i]为false,
//直接continue进行下次循环
continue;
}
path.add(candidates[i]);
sum=sum+candidates[i];
used[i]=true;//代表已经标记为true
isBackTest(candidates,target,sum,i+1,used);
//回溯
path.remove(path.size()-1);
sum=sum-candidates[i];
used[i]=false;//更新标记为false
}
}
}