タイトル説明(中難易度)
説明出力それらのすべての可能な順列そして、それはいくつかの数字与えられている、非常に簡単です。
ソリューションインサートA
これは彼が、まず、小さな問題を解決する方法を考える大きな問題を解決するために小さな問題を使用している検討するアイデアを考え始めた方法です。はい、それは再帰の考え方です。たとえば、
一つだけの数字[1]がある場合、それは簡単で、直接リターン[[1]] OKです。
あなたが追加した場合は1、2は、[12]はどのようにそれを行うには?我々は、わずか2挿入左側に権利が十分であること、1のギャップ内に、ケースの上部を必要とします。[なる2 1]、[1 2 ]。
我々は数3、[123]を追加する場合はどのようにそれを行うには?同様に、我々は唯一のラインでトップ3のギャップすべてのケースで数値を挿入する必要があります。例えば、[21]に左、中央及び右の挿入図3に示すように、上の3 2 1,2 3 1,2 1 3。同様に、左、中央及び右のインサート3、上の12 3。1 2,1 3 2,1 2 3、最終結果は、[321]、[231]、[213であります]、[312]、[132]、[123]。
私たちは番号を追加する場合は、同じ理由で、念の前に、デジタルギャップが新しい図を挿入するのに十分です。
コードで直接見てのアイデアを。
public List<List<Integer>> permute(int[] nums) {
return permute_end(nums,nums.length-1);
}
// end 表示当前新增的数字的位置
private List<List<Integer>> permute_end(int[] nums, int end) {
// 只有一个数字的时候
if(end == 0){
List<List<Integer>> all = new ArrayList<>();
List<Integer> temp = new ArrayList<>();
temp.add(nums[0]);
all.add(temp);
return all;
}
//得到上次所有的结果
List<List<Integer>> all_end = permute_end(nums,end-1);
int current_size = all_end.size();
//遍历每一种情况
for (int j = 0; j < current_size; j ) {
//在数字的缝隙插入新的数字
for (int k = 0; k <= end; k ) {
List<Integer> temp = new ArrayList<>(all_end.get(j));
temp.add(k, nums[end]);
//添加到结果中
all_end.add(temp);
};
}
//由于 all_end 此时既保存了之前的结果,和添加完的结果,所以把之前的结果要删除
for (int j = 0; j < current_size; j ) {
all_end.remove(0);
}
return all_end;
}
再帰的なプロセスがあるので、我々はまた、直接反復に、再帰は常に省略プッシュのプロセスを開始することができます。
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> all = new ArrayList<>();
all.add(new ArrayList<>());
//在上边的基础上只加上最外层的 for 循环就够了,代表每次新添加的数字
for (int i = 0; i < nums.length; i ) {
int current_size = all.size();
for (int j = 0; j < current_size; j ) {
for (int k = 0; k <= i; k ) {
List<Integer> temp = new ArrayList<>(all.get(j));
temp.add(k, nums[i]);
all.add(temp);
}
}
for (int j = 0; j < current_size; j ) {
all.remove(0);
}
}
return all;
}
時間の複雑さ、かなり複雑なコードワードの場合のみ分析。最終結果は、それがnであるべき場合!結果は、それが(N!)時間計算量Oをする必要があります。
複雑スペース:O(1)。
ソリューション2つのバック
スタートは、参照を考えていませんでしたここに。
実際には、それはあなたが一時に数を追加するたびに再帰的なバックトラックの非常に典型的な考えることができ、数字は、新しいソリューションを追加した後、再びバックトラック後で戻ってくるのに十分な追加します。
一つの層が追加されると理解することができ、それぞれの層は、forループです。
それぞれが、すべてのソリューションは、ループの等価リストに入り、その後、我々は必要なものを拾う呼び出します。実際には、本質的には、深さ優先探索のDFS。
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
backtrack(list, new ArrayList<>(), nums);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums){
if(tempList.size() == nums.length){
list.add(new ArrayList<>(tempList));
} else{
for(int i = 0; i < nums.length; i ){
if(tempList.contains(nums[i])) continue; // 已经存在的元素,跳过
tempList.add(nums[i]); //将当前元素加入
backtrack(list, tempList, nums); //向后继续添加
tempList.remove(tempList.size() - 1); //将 tempList 刚添加的元素,去掉,尝试新的元素
}
}
}
時間計算:
宇宙の複雑さ:
ソリューション3つの為替
リファレンス[ここ](https://leetcode.com/problems/permutations/discuss/18247/My-elegant-recursive-C - 溶液-と-インライン説明?のOrderBy = most_votes)。
アイデアは非常にクールされ、第1の再帰前の溶液、動的なプログラミング少し意味、デジタルソリューションに、解決策2桁、3桁のソリューションは、リングがリングを設定模索します。
関数は、すべての組み合わせのNUMSが生成され、すべてのアレイに追加することを要求する主題を実施することができると仮定する。しかし、それは、固定されたデジタルフロントを開始するには、[開始]それは、NUMSからのみ指定された数である、開始、1つの以上のパラメータです。
upset(int[] nums, int begin, List<List<Integer>> all)
そのようなA機能がある場合、すべてが簡単です。
あなたがNUMSの長さに等しい始める場合、それはすべて同じ開始する前に、それは、すべての数字と同じである数のことを意味し、私たちはライン上のすべてにそれを追加する必要があります。
if (begin == nums.length) {
ArrayList<Integer> temp = new ArrayList<Integer>();
for (int i = 0; i < nums.length; i ) {
temp.add(nums[i]);
}
all.add(new ArrayList<Integer>(temp));
return;
}
他の例は、私たちが実際に一度だけ、各番号に始まり、その後、十分後ろの番号を変更するには、forループを使用する必要がある場合は、動揺の関数呼び出しで、すべての組み合わせが最初から1を開始します。
for (int i = begin; i < nums.length; i ) {
swap(nums, i, begin);
upset(nums, begin 1, all);
swap(nums, i, begin);
}
全体のケースです。
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> all = new ArrayList<>();
//从下标 0 开始的所有组合
upset(nums, 0, all);
return all;
}
private void upset(int[] nums, int begin, List<List<Integer>> all) {
if (begin == nums.length) {
ArrayList<Integer> temp = new ArrayList<Integer>();
for (int i = 0; i < nums.length; i ) {
temp.add(nums[i]);
}
all.add(new ArrayList<Integer>(temp));
return;
}
for (int i = begin; i < nums.length; i ) {
swap(nums, i, begin);
upset(nums, begin 1, all);
swap(nums, i, begin);
}
}
private void swap(int[] nums, int i, int begin) {
int temp = nums[i];
nums[i] = nums[begin];
nums[begin] = temp;
}
時間計算:
宇宙の複雑さ:
全体的な
この質問は、バックトラック、再帰の実装は非常に古典的な、ダイナミックプログラミングを再び、当然のことながら、一瞬のための再帰的な解決策を強制するために、そして実際に3つのソリューションに比べて見劣りされ、再帰的なソリューションの3つは本物の、シンプルかつエレガントです。
人気のより詳細な説明についてはleetcode.wang。