「創造を続け、成長を加速させましょう!これは、「ナゲッツデイリーニュープラン・6月のアップデートチャレンジ」に参加してから6日目です。クリックしてイベントの詳細を表示してください。」
序文
以前、Xiao Liuliuは常に彼のアルゴリズムが比較的悪いと感じていて、それは欠点でした。以前は、実際には3日間の釣り、2セットのネット、数日間のブラッシングでしたが、その後ゆっくりと停止しました。この第二に、プラットフォームの活動の助けを借りて、ゆっくりとブラッシングを開始する予定です。また、ブラッシングの質問を要約し、自分の考えやアイデアなどについて話します。これが私の助けになることを願っています。友達。この機会にあなたの欠点を補うこともできます。あなたがそれに固執できることを願っています。
トピック
組み合わせ合計III216の質問
合計がnになり、次の条件を満たすk個の数のすべての組み合わせを見つけます。
1から9までの数字のみを使用してください各数字を最大で1回使用してくださいすべての可能な有効な組み合わせのリストを返します。リストに同じ組み合わせを2回含めることはできません。組み合わせは、任意の順序で返すことができます。
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
复制代码
分析する
実際、この問題を見ることができます。これは、バックトラッキングアルゴリズムによっても解決されます。過去数日間、Liuliuはすべてのバックトラッキングアルゴリズムを共有しました。バックトラッキングアルゴリズムの定義については、上記の記事をご覧ください。テンプレートを作成すると、誰もが単語を入力するようなもので、内部のコードを入力してみてください。
ステンシル
ふりかえり三部作
- 1.バックトラッキングのシングルレベルロジックを決定します
forループを介して現在の位置(つまり、現在の再帰層)を制御する数値は、1から9まで選択できます。数値が選択されるたびに(数値へのトラバースごとに)、ターゲットからその値を減算します。合計と加算を同時に行います。数字の組み合わせのセットを記録します。前の手順で、最初の桁を決定し、次に次の桁を再帰的に決定しました。次のレイヤーで次の桁が始まる位置に注意してください。前のレイヤーで選択した番号は次のレイヤーでは選択できないため、次の桁を再帰的に選択する必要があります。特定のレイヤーに再帰すると、この時点でk個の番号が決定されていることがわかり、判断する必要があります。これらのk個の数値の合計がnに等しいかどうか、nに等しい場合は、今回収集した組み合わせを最終結果セットに追加し、現在の再帰レイヤーを終了して、バックトラックの準備をします。それ以外の場合は、再帰を直接終了し、バックトラックの準備をします。前の再帰レイヤーから出たら、バックトラックします。つまり、削除します。最後の桁を削除して新しい桁を選択します。
- 2.バックトラッキング関数のパラメーターと戻り値を決定します
上記の分析によれば、再帰の各層のターゲット合計の値を更新し、k個の数値が現在選択されているかどうか、および現在の再帰の層の数値の開始点をどこで選択する必要があるかを判断することがわかります。したがって、次のパラメータが必要です。ターゲットの合計、質問で指定されたk、現在のレイヤーの再帰的選択番号の開始位置2つのグローバル変数を使用して現在の組み合わせと最終結果セットを記録するため、値を返す必要はありません
- 3.バックトラッキング機能の終了条件を決定します
シングルレベルロジックを分析すると、すでに次のようになります。k個の数値を選択すると、現在の再帰が終了し、次のようになります。
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
复制代码
答え
package com.six.finger.leetcode.five;
/*
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
*/
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class fifteen {
public static void main(String[] args) {
combinationsum(9, 45);
}
public static List<List<Integer>> combinationsum(int k, int n) {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
backtracking(res, path, k, n, 1);
return res;
}
private static void backtracking(List<List<Integer>> res, LinkedList<Integer> path, int k, int n, int i) {
int sum = 0;
//第一步,递归的退出条件
for (Integer integer : path) {
sum = sum + integer;
}
if (sum > n) {
return;
}
if (sum == n && path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
//第二步 树的横向长度,和减枝活动
for (int j = i; j <= 9 - (k - path.size()) + 1; j++) {
path.add(j);
backtracking(res, path, k, n, j + 1);
path.removeLast();
}
}
}
复制代码
提出記録を見て、私はそれを最適化する方法を考えるのが面倒です
最適化
実際、パスの合計を毎回計算し、変更して、次のように計算する必要があるのはなぜですか?
for (int j = i; j <= 9 - (k - path.size()) + 1; j++) {
sum=sum+i;
path.add(j);
backtracking(res, path, k, n, j + 1,sum);
path.removeLast();
sum=sum-i;
}
复制代码
それならダメ
終了
さて、今日はこの質問は終わりです。1つのタイプを続けて練習すると、実際、このタイプの質問は徐々に難しくなることがわかりました。以前はアルゴリズムは難しいと思っていましたが、実際にはすべてに私たちは法のルールを要約することしかできません。新しいものを作成することはできませんが、それでも他の人の経験から学ぶことができます。さあ!!