1.指数型枚举
实际问题:1-n个数字中随机选取多个,每种方案按照里的数字按照从小到大的顺序排列,按照字典序输出。
输入 :3
输出:1,12,123,2,23,3
解决问题三步骤:
1.如何枚举每个位置的数字?
2.每个位置的数字都要大于前面位置的数字?
3.如何输出这些结果?
解决:
1.想象成 n 个格子需要填,每个格子都从小到大的枚举数字。
2.传入一个变量,可以标记当前位置最小可以选取的数字
3.用函数输出结果
代码如下:
import java.util.Scanner;
public class Main {
// 用于存储每个位置的数字
private static int[] arr;
public Main(int maxNum) {
arr = new int[maxNum];
}
/**
*递归函数实现指数型枚举
* @param currentIndex 枚举第i位的数字,从0开始
* @param currentMinNum 当前位置最小选择的数字,从1开始
* @param maxNum 最大可以选取的数字,用户输入
*/
public void function(int currentIndex, int currentMinNum, int maxNum) {
// 如果当前可以选取的最小数字大于n,则返回
if (currentMinNum > maxNum) return;
for (int i = currentMinNum; i <= maxNum; i++) {
// 将当前位置可以填的数字赋值给当前位置
arr[currentIndex] = i;
// 打印结果
printResult(currentIndex);
// 递归调用
function(currentIndex + 1, i + 1, maxNum);
}
}
/**
* 打印结果
* @param rightLimit 打印数组的右边界
*/
private void printResult(int rightLimit) {
for(int i = 0; i <= rightLimit; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
// 读入一个可以读取的最大数字
Scanner sc = new Scanner(System.in);
System.out.println("请输入边界数字:");
int maxNum = sc.nextInt();
Main main = new Main(maxNum);
main.function(0, 1, maxNum);
}
}
2.组合型枚举
实际问题:1-n个数字中随机选取 m 个,每种方案按照里的数字按照从小到大的顺序排列,按照字典序输出。
例如:
输入:
5 3
输出:
123
124
125
134......
345
和指数型枚举很相似,只不过指数型是全枚举,而组合型枚举是有限制的枚举,我们枚举到第 m 位才进行输出。
代码如下:
import java.util.Scanner;
public class Main {
// 用于存储每个位置的数字
private static int[] arr;
public Main(int rightLimit) {
arr = new int[rightLimit];
}
/**
*递归函数实现组合型枚举
* @param currentIndex 枚举第i位的数字,从0开始
* @param currentMinNum 当前位置最小选择的数字,从1开始
* @param maxNum 最大可以选取的数字,用户输入
* @param indexLimit numLimit 当前已经选取的数字数量
*/
public void function(int currentIndex, int currentMinNum, int maxNum, int indexLimit) {
// 如果已经选取的数字数量等于numLimit,则返回
if (currentIndex == indexLimit) {
printResult(indexLimit);
return;
}
// 当枚举到第i位置时,剩余没有枚举的数字数量要大于等于要枚举的位置数量,进行剪枝策略,去掉不必要的枚举
for (int i = currentMinNum; i <= maxNum && indexLimit - currentIndex - 1 <= maxNum - i; i++) {
// 将当前位置可以填的数字赋值给当前位置
arr[currentIndex] = i;
// 递归调用
function(currentIndex + 1, i + 1, maxNum, indexLimit);
}
}
/**
* 打印结果
* @param rightLimit 需要打印的右边界
*/
private void printResult(int rightLimit) {
for(int i = 0; i < rightLimit; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
// 读入一个可以读取的最大数字
Scanner sc = new Scanner(System.in);
System.out.println("请输入边界数字:");
int maxNum = sc.nextInt();
System.out.println("请输入选取枚举数字数量:");
int indexLimit = sc.nextInt();
Main main = new Main(indexLimit);
main.function(0, 1, maxNum, indexLimit);
}
}
3.排列型枚举
实际问题:1-n个这 n 个整数拍成一排并打乱次序,按照字典序输出所有可以选择的方案。
例如:
输入:
3
输出:
123
213
231
312
321
关键:
(1)全排列
(2)排列型枚举中 枚举位置的数字 必须是 前面没有出现过的 数字。
(3)后面的数字不必比前面的数字小
组合数量计算,第一个位置有 n 种可能,第二为位置有n-1种可能(因为第一个位置一定要使用一个数字)...第n个位置只有1种可能(只剩一个数字可以填)
因为后面的数字 不必 比前面的数字大,所以每个位置的数字都是全排列的,但是这样数字会重复;要求不能重复,所以要在全排列中 剔除 重复的组合,所以要额外建立一个数组,数组的下标是对应的数字,下标对应的值是 0 或 1,对应该下标的数字 是否 被使用过。指数型枚举 和 组合型枚举 因为 后面的数字 要比 前面的数字 大,所以数字一定不会重复,因此不需要构建这种数组。
代码如下:
import java.util.Scanner;
public class Main {
// 用于存储每个位置的数字
private int[] arr1;
// 用于标记数字是否使用过,当arr2[i]为1时,表示i这个数字被使用过了
private int[] arr2;
public Main(int rightLimit) {
arr1 = new int[rightLimit];
arr2 = new int[rightLimit +1];
}
/**
*递归函数实现排列型枚举
* @param currentIndex 枚举第i位的数字,从0开始
* @param maxNum 最大可以选取的数字,用户输入
*/
public void function(int currentIndex, int maxNum) {
// 如果已经选取的数字数量等于numLimit,则返回
if (currentIndex == maxNum) {
printResult(maxNum);
return;
}
for (int i = 1; i <= maxNum; i++) {
// 如果数字i已经使用过,则跳过
if (arr2[i] == 1) {
continue;
}
// 将当前位置可以填的数字赋值给当前位置
arr1[currentIndex] = i;
// 标记i这个数字已经使用过
arr2[i] = 1;
// 递归调用
function(currentIndex + 1, maxNum);
// 当前位置数字为i的所有组合已经枚举完毕,回收标记
arr2[i] = 0;
}
}
/**
* 打印结果
* @param rightLimit 需要打印的右边界
*/
private void printResult(int rightLimit) {
for(int i = 0; i < rightLimit; i++) {
System.out.print(arr1[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
// 读入一个可以读取的最大数字
Scanner sc = new Scanner(System.in);
System.out.println("请输入边界数字:");
int maxNum = sc.nextInt();
Main main = new Main(maxNum);
main.function(0, maxNum);
}
}
总结:
相同思维:
枚举函数的构造方式:
第i个位置枚举的全可能性数字 + 后面的枚举数字的全可能性
不同点:
指数型枚举: 是 全排列,从第一个位置开始,每次多枚举一个位置的数字,每次枚举都输出,每次后面枚举的数字 比 前面的数字 大(所以指数型枚举的数字一定不会重复)
组合型枚举:不是全排列,枚举到指定 大小的 位置时才输出
排列型枚举:是 全排列,但是 枚举的数字 前后没有大小关系,如果不限制数字会重复,所以需要一个记录数字是否使用过的数组,来实现数字不能重复。