1. 再帰的バックトラッキングアルゴリズム
1. 再帰の考え方
再帰とは、メソッドがそれ自体を呼び出し、呼び出されるたびに異なる変数を渡すことを意味します。
2. 再帰の原理
1) メソッドが実行されるたびに、独立した[スタックメモリ]に空間が確保されます。
2) 【基本データ型】の場合、各空間の変数はローカル変数となり互いに【独立】します。
3) [参照データ型]の場合、アドレス値が格納されるため各空間の変数は[共有]され、新規オブジェクトは[ヒープメモリ]に格納され[アドレス値]が割り当てられます。が実行されると、アドレス値はすべて同じものを指すため、スタック空間に保存された変数アドレス値に基づいてヒープ メモリ内で同じ変数が見つかります。
4) 各再帰の後、条件は [継続的に収束] する必要があります。つまり、再帰を終了することが保証されている必要があります。そうでない場合、再帰は終了せず、スタック メモリにスペースが割り当てられ続け、[スタック オーバーフロー] が発生します。問題。
2. 配置の問題
1. 配置式
A_n_m = n*(n-1)*(n-2)*...*(n-m+1)=n!/(n-m)!
2. A_4_4 = 4 3 2*1=4 とします。例えば:
1) 準備作業
1)创建一个集合prelist:[1,2,3,4]
2)创建一个存储排列结果的集合preList
3)定义一个从preList中遍历取元素,然后添加元素到preList集合中的方法:setNum
2) setNum メソッドをメソッド内を含めて再帰的に呼び出します。
1)递归结束的条件:向preList集合中添加4个数,就结束递归,并打印结果
2)判断过程
【遍历】preList集合,取出preList集合中的第一个元素,如果resList集合中没有该元素,就添加到resList集合;
否则,继续取出preList集合第二个元素,依次类推,直到元素可以添加到resList集合中。
3)如果经过判断,某个元素添加成功了,则【递归】调用setNum方法,继续添加下一个元素
3) バックトラッキング
[最初の解] [1,2,3,4] が見つかったら、前のスタックに [戻る] このとき、[スタックの先頭] 要素は 3 です。4 を試すこともできますので、再度 Setスタックの最上位の要素を 4 に設定し、[再帰的に] 最後の数値を設定します。明らかに、3 のみです。したがって、[2 番目の解] は [1,2,4,3] となります。
2番目の解決策を見つけた後、前のスタックに[戻る]、番号は3か4しか設定できないため、他の選択肢はありません、前のスタックに[フォールバックし続ける]、現在の番号は2、まだ 3 または 4 を選択できるので、最初に 3 に設定してから [再帰的に] 他の数値を設定し、4 に設定してから他の数値を再帰的に設定します。
最終的には、resListコレクションの中から[先頭要素が1である配置結果が全て見つかる]ことになります。
次に、 resList の要素として 2 を設定すると、2 で始まるすべての順列結果が見つかり、以下同様に 3 と 4 で始まる順列結果が見つかります。
4) 最終結果が出力される順序は、上記で分析した順序です。つまり、次のとおりです。
[1,2,3,4]
[1,2,4,3]
[1,3,2,4]
[1,3,4,2]
5) 注: 前のスタックに戻るときに、他の数値を試すことができる理由は、再帰するたびにプレリスト配列が [走査] されるためです。
3.Javaコード
public class Permutation {
static Stack<Integer> stack = new Stack<>();
public static void main(String[] args) {
int res = permutation(4, 3);
System.out.println("A_4_3的结果为:" + res);
List<Integer> preList = new ArrayList<>();
preList.add(1);
preList.add(2);
preList.add(3);
preList.add(4);
System.out.println("preList=" + preList);
enumerration(preList, 4, 0);
}
/**
* 全排列公式实现
* @param n 一共有n个
* @param m 从n中取m个
* @return 表示一共有多少种取法
*/
public static int permutation(int n, int m){
int res = 1;
/*
A_4_2 = 4 * 3 循环两次实现:4*1*3
A_4_3 = 4 * 3 * 2 循环三次实现:4*1*3*2
A_n_m 需要循环m次实现:n*1*(n-1)*...*(n-m+1)
*/
for (int i = 0; i < m; i++) {
res *= n;
n--;
}
return res;
}
/**
* 列举出排列结果
* 例如A_4_4,排列结果就是在给的四个数中依次不重复地选择一个数
* @param preList 存放需要排列的数据的列表
* @param total 表示一共选几次数
* @param cur 表示当前是第几次选数
*/
public static void enumerration(List<Integer> preList, int total, int cur){
if (cur == total){
System.out.println(stack);
return;
}
for (Integer item : preList) {
if (!stack.contains(item)){
stack.push(item);
enumerration(preList, total, cur+1);
stack.pop();
}
}
}
}
3. 8人の女王の問題
1.問題の説明
2 つのクイーンを同じ行、列、または対角線上に配置することはできません。方法は何通りありますか?
スパース配列の考え方に従って、2 次元配列で表されるチェス盤を 1 次元配列に圧縮します。
arr[i]=値。i は i+1 番目のクイーンを表し、value はこのクイーンが i+1 行目、i+1 列目に配置されることを表します。
2. アイデア分析
1) 順列問題と同様に、最初のクイーンを最初の行と最初の列に配置し、最初の正しい解が生成されるまでクイーンを配置するメソッドを再帰的に呼び出します。
2) 最初の正しい解が生成されたら、前のスタック (つまり、最後から 2 番目の行) に戻り、他の位置を試してから、クイーンを配置するメソッドを呼び出し、最後の行のクイーンを設定します。最初のクイーンを最初の行と最初の列に配置するためのすべての正しい解決策
3) 次に、最初のクイーンを最初の行と 2 列目に配置します。。。
3.Javaコード
在这里插入代码片