[再帰+バックトラッキング]配列要素の組み合わせ、配置、完全な配置を実現します

目次

1つは、配列要素の組み合わせです。

第二に、配列要素の完全な配置

第三に、配列要素の順列と組み合わせ

こんにちは、お元気ですか、私は小さな灰色の類人猿です!バグを書くことができるスーパープログラマー!

最近、ブルーブリッジカップ関連のテスト質問を行うと、配列要素の順列と組み合わせの使用が非常に広範囲であり、一般的な順列と組み合わせタイプの質問もデータ構造とアルゴリズムの典型的な例であることがわかりました。今日は共有します通常の開発プロセスでは、順列と組み合わせのいくつかのタイプとソリューションがよく使用されます。

1つは、配列要素の組み合わせです。

m個の数値がn要素の配列arrから取得され(順序に関係なく、繰り返しがない)、新しい配列newarrに配置される場合、一般的な考え方は再帰的な考え方を使用することです。

  1. 配列arrからn個の数値を取得すると、最初のarrの数値をnewarrの最初の要素として取得できます。
  2. arrの最初の要素を取り出した後、再帰的な実装を使用して、次のn-1要素からm-1要素を取り出します(これは最初のステップのサブ問題です)
  3. 0個の要素を取り出す必要がある場合、結合されたタスクが完了します
  4. 最初のステップに戻り、forループを使用して2番目の要素を取り出し(次の組み合わせを開始)、合計でnm回ループします。

特定の実装は、直接呼び出して使用できる次の関数で表示できます。

    /**
	 * 在数组中选取n个数进行组合(不考虑顺序且数据不重复)
	 * @param 待处理的数组
	 * @param newarr 组合后得到的数组
	 * @param k  从哪一个下标的元素开始取
	 * @param n 需要取出元素的个数
	 * */
	private static void combination(int[] arr,int[] newarr, int k,int n) {
		//当需要取出的元素个数是0时,说明组合完成
		if (n==0) {
			for (int i = 0; i < newarr.length; i++) {
				System.out.print(newarr[i]);
			}
			System.out.println();
			return;
		}
		for (int i = k; i <= arr.length-n; i++) {		
			newarr[newarr.length-n] = arr[i];	//将提取出来的数依次放到新数组中
			combination(arr, newarr,i+1, n-1);	//按照同样的方法从剩下的元素中选出n-1个元素
		}
	}

テストケース:

public static void main(String[] args) {
		int[] arr = {1,2,3,4};		//待处理的数组
		int n = 3;	//取出元素的个数
		int[] newarr = new int[n];		//存放结果的数组
		combination(arr, newarr, 0, n);
	}

第二に、配列要素の完全な配置

n個の数値を持つ配列arrを完全に配置するために使用されるアイデアは、再帰とバックトラックです。

  1. n個の要素を完全に配置し、最初の要素を次の要素と順番に交換して、最初の要素を決定します
  2. 次のn-1要素の完全な配置(最初のステップのサブ問題と見なすことができます)は、再帰によって実装されます。
  3. 配列要素の順序が乱れるのを防ぐために、交換された要素を元に戻します(バックトラック思考)

特定の実装は、次の関数を見ることができます(直接使用できます)

    /**
	 * 对数组中所有的元素进行全排列
	 * @param arr 待排列的数组
	 * @param k 确定第几个元素,是下标,从0开始
	 * */
	private static void f(int[] arr, int k) {
		//当k等于数组的长度时,说明排列完成
		if (k == arr.length) {
			//将排列好的数组输出
			for (int i = 0; i < arr.length; i++) {
				System.out.print(arr[i]);
			}
			System.out.println();
		}
		
		for (int i = k; i < arr.length; i++) {
			//将待确定的元素位置和后面的元素互换
			int t = arr[k];
			arr[k] = arr[i];
			arr[i] = t;
			
			//递归(确定第k+1个元素)
			f(arr, k+1);
			
			//回溯,将调换后的元素重新调换回来
			t = arr[k];
			arr[k] = arr[i];
			arr[i] = t;
		}	
	}

テストケース:

public static void main(String[] args) {
		int[] arr = {1,2,3,4};		//待处理的数组
		int n = 3;	//取出元素的个数
		int[] newarr = new int[n];		//存放结果的数组
		f(arr, 0);
	}

第三に、配列要素の順列と組み合わせ

n要素配列arrからm個の数を取り出し(順序に関係なく、繰り返しなし)、n個の数を完全に並べ替え、次にn個の数からm個の数を取り出して並べ替えを実現するという上記の理解により、問題は次の組み合わせとして見ることができます。上記の2つの問題。

数学の考え方によれば、最初にn個の要素の配列からm個の要素を選択し、次にすべてのm個の要素を配置することができます。

実装方法は次のとおりです。

    /**
	 * 数组中对n个数进行全排列
	 * @param 待处理的数组
	 * @param newarr 排列后得到的数组
	 * @param k  从哪一个下标的元素开始处理
	 * @param n 处理元素的个数
	 * */
	private static void pac(int[] arr,int[] newarr, int k,int n) {
		//当n=0时,说明选取的数的个数为0,也就是组合完成
		if (n==0) {
			f(newarr, 0);	//对组合到的新数组进行全排列
			return;
		}
		for (int i = k; i <= arr.length-n; i++) {		
			newarr[newarr.length-n] = arr[i];
			pac(arr, newarr,i+1, n-1);
		}
	}
	
	/**
	 * 对数组中所有的元素进行全排列
	 * @param arr 待排列的数组
	 * @param k 确定第几个元素,是下标,从0开始
	 * */
	private static void f(int[] arr, int k) {
		//当k等于数组的长度时,说明排列完成
		if (k == arr.length) {
			//将排列好的数组输出
			for (int i = 0; i < arr.length; i++) {
				System.out.print(arr[i]);
			}
			System.out.println();
		}
		
		for (int i = k; i < arr.length; i++) {
			//将待确定的元素位置和后面的元素互换
			int t = arr[k];
			arr[k] = arr[i];
			arr[i] = t;
			
			//递归(确定第k+1个元素)
			f(arr, k+1);
			
			//回溯,将调换后的元素重新调换回来
			t = arr[k];
			arr[k] = arr[i];
			arr[i] = t;
		}	
	}

テストケース:

public static void main(String[] args) {
		int[] arr = {1,2,3,4};		//待处理的数组
		int n = 3;	//取出元素的个数
		int[] newarr = new int[n];		//存放结果的数组
		pac(arr,newarr,0, n);
	}

上記は、順列と組み合わせの3つの一般的なタイプとそれらのソリューションです。主なアイデアは、再帰とバックトラックを使用することです。最適化や不備がありますので、修正していただければ幸いです。

気分が良く、好きでフォローすることを忘れないでください!

リトルグレイエイプは一緒に進歩するためにあなたに同行します!

おすすめ

転載: blog.csdn.net/weixin_44985880/article/details/113434593