再帰的バックトラッキングアルゴリズム

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コード

在这里插入代码片

おすすめ

転載: blog.csdn.net/weixin_42214237/article/details/123392907