【递归+回溯】实现数组元素的组合、排列和全排列

目录

一、数组元素的组合

二、数组元素的全排列

三、数组元素的排列组合

Hello,你好呀,我是灰小猿!一个超会写bug的程序猿!

最近在做蓝桥杯相关的试题的时候发现对数组元素进行排列组合的使用十分的广泛,而常见的排列组合类型的题目也是数据结构和算法的典型例题,所以今天在这里和大家分享一下我们在平常的开发过程中,常会用到的几种排列组合的类型和解法:

一、数组元素的组合

对于从n个元素的数组arr中取出m个数(不考虑顺序且不重复)放到新数组newarr中的情况,常见的思路是使用递归的思想:

  1. 从数组arr中取出n个数,那么我们可以先取出arr的第一个数作为newarr的第一个元素
  2. 取出arr的第一个元素之后,从后面的n-1个元素中取出m-1个元素,(这是第一步的子问题)采用递归实现。
  3. 当需要取出0个元素时,一个组合的任务完成
  4. 回到第一步,利用for循环接着取出第二个元素(开始下一个组合),一共循环n-m次即可

具体的实现可以查看下面的函数,可直接调用使用:

    /**
	 * 在数组中选取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个数实现排列的问题,可以看成是上面两个问题的结合体。

按照数学中的思路,我们可以先从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);
	}

以上就是我们常见的三种排列组合类型及其解决方法。主要就是采用了递归和回溯的思想。其中有优化或不足的地方还希望各位提出更正。

觉得不错记得点赞关注哟!

灰小猿陪你一起进步!

猜你喜欢

转载自blog.csdn.net/weixin_44985880/article/details/113434593