一、说明
在牛客网的很多算法试题中,很多试题底层都是基于排列组合算法实现的,比如动态规划、最优解、最大值等常见问题。排列组合算法有一定的难度,并不能用一般的多重嵌套循环解决,没有提前做针对性的学习和研究,考试时候肯定是事倍功半,所以今天我们专门出一篇文章来讲一下这个问题。
排列个数的计算公式如下:
组合个数的计算公式如下:
二、放回再取问题
1、问题1:
假设袋子里有编号为1,2,...,m这m个球。现在每次从袋子中取一个球几下编号,放回袋中再取,取n次作为一组,枚举所有可能的情况。
分析:每一次取都有m种可能的情况,因此一共有种情况。
package xu.com.coder.test;
import java.util.Stack;
/**
* @author xulijun
*/
public class Recursion1Test {
static Stack<Integer> stack = new Stack<Integer>();
static int count = 0;
public static void main(String[] args) {
int[] data = {1, 2, 3, 4};
recursion(data, 3, 0);
System.out.println(count);
}
/**
* 递归方法,当实际选取的数目与要求选取的数目相同时,跳出递归
*
* @param dataArray 数组
* @param selectNum 要选取的数目
* @param curNum 当前已经确定的个数
*/
public static void recursion(int[] dataArray, int selectNum, int curNum) {
if (curNum == selectNum) {
count++;
System.out.println(stack);
return;
}
for (int item : dataArray) {
stack.push(item);
recursion(dataArray, selectNum, curNum + 1);
stack.pop();
}
}
}
运行结果:
三、排列问题
问题2:
假设袋子里有编号为1,2,...,m这m个球。先后从袋子中取出n个球,依次记录编号,不同顺序是一组不同的排列,枚举所有可能的情况。
分析:这是排列问题,应该有种情况。
package xu.com.coder.test;
import java.util.Stack;
/**
* @author xulijun
*/
public class Recursion2Test {
static Stack<Integer> stack = new Stack<Integer>();
static int count = 0;
public static void main(String[] args) {
int[] data = {1, 2, 3, 4};
recursion2(data, 3, 0);
System.out.println(count);
}
/**
* 递归方法,当实际选取的数目与要求选取的数目相同时,跳出递归
*
* @param dataArray 数组
* @param selectNum 要选取的数目
* @param curNum 当前已经确定个数
*/
public static void recursion2(int[] dataArray, int selectNum, int curNum) {
if (curNum == selectNum) {
count++;
System.out.println(stack);
return;
}
for (int item : dataArray) {
if (!stack.contains(item)) {
stack.push(item);
recursion2(dataArray, selectNum, curNum + 1);
stack.pop();
}
}
}
}
运行结果:
四、组合问题
问题3:
从m个球里(编号为1,2,3...,m)一次取n个球,其中m>n,记录取出球的编号,不同的顺序是同一种组合,枚举所有的可能性。
分析:这是组合问题。因该有种可能性。
package xu.com.coder.test;
import java.util.Stack;
/**
* @author xulijun
*/
public class Recursion3Test {
static Stack<Integer> stack = new Stack<Integer>();
static int count = 0;
public static void main(String[] args) {
int[] data = {1, 2, 3, 4};
recursion3(data, 3, 0, 0);
System.out.println(count);
}
/**
* 递归方法,当实际选取的数目与要求选取的数目相同时,跳出递归
*
* @param dataArray 数组
* @param selectNum 要选取的数目
* @param curNum 当前已经确定的个数
*/
public static void recursion3(int[] dataArray, int selectNum, int curNum, int indexNum) {
if (curNum == selectNum) {
count++;
System.out.println(stack);
return;
}
for (int i = indexNum; i < dataArray.length; i++) {
if (!stack.contains(dataArray[i])) {
stack.push(dataArray[i]);
recursion3(dataArray, selectNum, curNum + 1, i);
stack.pop();
}
}
}
}
运行结果:
补充说明:
又的读者可能说,上面的代码太复杂看不懂,这里顺嘴提一个特别简单大家看的懂的办法;
1、比如从1、2、3、4中选2个作为一组组合
package xu.com.coder.test;
/**
* @author xulijun
*/
public class Recursion3Test_1 {
static int count = 0;
public static void main(String[] args) {
int[] data = {1, 2, 3, 4};
recursion3(data, 3);
System.out.println(count);
}
public static void recursion3(int[] dataArray, int selectNum) {
for (int i = 0; i < dataArray.length; i++) {
for (int j = i + 1; j < dataArray.length; j++) {
count++;
System.out.println("[" + dataArray[i] + "," + dataArray[j] + "]");
}
}
}
}
2、比如从1、2、3、4中选3个作为一组组合
package xu.com.coder.test;
/**
* @author xulijun
*/
public class Recursion3Test_1 {
static int count = 0;
public static void main(String[] args) {
int[] data = {1, 2, 3, 4};
recursion4(data, 3);
System.out.println(count);
}
public static void recursion4(int[] dataArray, int selectNum) {
for (int i = 0; i < dataArray.length; i++) {
for (int j = i + 1; j < dataArray.length; j++) {
for (int m = j + 1; m < dataArray.length; m++) {
count++;
System.out.println("[" + dataArray[i] + "," + dataArray[j] + "," + dataArray[m] + "]");
}
}
}
}
}
这个办法非常简单,应该一般人都能看得懂,但是最大的问题是我们必须要先知道从中选择几个,否则我们的循环层级是不确定的,但是考试时候可以用这个方法可以用来应急。