文章目录
一维指数型枚举
一、01型枚举
应用场景
- 给定x个方格,每个方格可以存放0或者1两个状态
- 可以理解为求出data集合的幂集所有元素
递归树模型
- 标准的二叉树,每个分支表示对于同一个方格的所有不同状态选择情况
时间复杂度
-
O
(
x
2
)
O(x^2)
O(x2)
代码模版
public class Main {
// 待枚举的数据长度
int n;
// 存放每次的选取状态,1表示选,0表示不选
int[] state;
// 待枚举的数据
Object[] data = {'上', '下', '左', '右'};
void run() {
n = data.length;
state = new int[n];
dfs(0);
}
// i表示当前正在考虑state中的下标i的位置的存放状态
void dfs(int i) {
// 进入该条件语句内,表示已经递归完了递归树的叶子节点,即已经填满了state数组
if (i > n - 1) {
/* ==== 根据题目条件来利用state ==== */
return;
}
/* ====== 以下为考虑当前方格状态取值的代码 ====== */
state[i] = 1;
dfs(i + 1);
state[i] = 0;
dfs(i + 1);
}
public static void main(String[] args) {
new Main().run();
}
}
应用
二、滑动型枚举
应用场景
- 给定m个格子,每个格子可能为给定的n个数据中的某一个(可以重复)
递归树模型
- 标准的m叉树
时间复杂度
- O ( x m ) O(x^m) O(xm)
代码模版
public class Main {
Scanner s = new Scanner(System.in);
// 格子数量
int n;
// 存放每一次枚举出来的某种情况
Object[] container;
// 所给的数据,根据题目改变,下面为例子
Object[] data = {'上', '中', '下', '前', '后', '左', '右'};
void run() {
n = s.nextInt();
container = new Object[n];
dfs(0);
}
// i表示当前正在考虑container的哪一个下标所取值
void dfs(int i) {
if (i > n - 1) {
// 根据题目来使用container
return;
}
// 这里表示container的下标i处位置会有data.length种情况取值
for (int k = 0; k < data.length; k++) {
container[i] = data[k];
dfs(i + 1);
}
}
public static void main(String[] args) {
new Main().run();
}
}
排列型枚举
应用场景
-
给定n个数,从中任意取m个(m <= n),枚举出所有情况(考虑顺序)
-
给定m个格子,每个格子可能为给定的n个数据中的某一个(不能重复)
对比一下滑动型枚举:给定m个格子,每个格子可能为给定的n个数据中的某一个(可以重复)
递归树模型
- 随着选取的数字增多,每一层节点的分支减1
时间复杂度分析
第一层:1个节点执行一次for循环,O(n)
第二层:n个节点,每个节点都执行一次for循环,O(n*n)
第三层:n*(n-1)个节点,每个节点都执行一次for循环,O(n*(n-1)*n)
…
倒数第二层:n*(n-1)*(n-2)*… 2 个节点,同理,O(n*(n-1)*(n-2)* … *2*n)
最后一层:n!个节点,每个节点不用再执行一次for循环,但是需要输出数据,O(n!*n)
总复杂度: O ( ( n ∗ ( 1 + n ∗ ( n − 1 ) + n ∗ ( n − 1 ) ∗ ( n − 2 ) + . . . + n ! ) ) ) O((n*(1+n*(n-1)+n*(n-1)*(n-2)+...+n!))) O((n∗(1+n∗(n−1)+n∗(n−1)∗(n−2)+...+n!))),大概为 O ( n ∗ n ! ) O(n*n!) O(n∗n!)
代码模版
public class Main {
Scanner s = new Scanner(System.in);
// m个格子
int m;
// 存放每次排列的结果
Object[] container;
// 待排列的数据(这里以下面几个字符为例),相当于n个数据
Object[] data = {'上', '中', '下', '前', '后', '左', '右'};
int n = data.length;
// dfs过程中记录data数组哪个数据已经被存放在container中
// used数组下标映射data数组的下标
boolean[] used;
void run () {
m = s.nextInt();
container = new Object[m];
used = new boolean[n];
dfs(0);
}
// i表示当前正在考虑container中的下标i的位置存放什么元素
void dfs(int i) {
// 边界:进入下面语句内表示container中已经填满元素,表示某一种排列
if (i > m - 1) {
// 在这里根据题意使用container
return;
}
// 该循环表示在container考虑到下标i位置时,所有分支情况
for (int k = 0; k < n; k++) {
// 如果在data数组中下标为k的元素还没有被使用
if (!used[k]) {
// 把data[k]放入container中,更新used数组,考虑container下一个位置
container[i] = data[k];
used[k] = true;
dfs(i + 1);
container[i] = '\u0000';
used[k] = false;
}
}
}
public static void main(String[] args) {
new Main().run();
}
}
应用
组合型枚举
应用场景
- 给定n个数,从中任意取m个(m <= n),枚举出所有情况(不考虑顺序)
递归树模型
- 从第二层的节点开始,从左到右每一个子树的所有节点依次递减
代码模版
public class Main {
Scanner s = new Scanner(System.in);
// m个格子
int m;
// 给定的数据,根据题目改变
Object[] data = {'上', '中', '下', '前', '后', '左', '右'};
int n = data.length;
// 保存每次枚举的情况
Object[] container;
// 从下标几开始考虑分支
int start;
void run() {
m = s.nextInt();
container = new Object[m];
dfs(0);
}
// i表示container正在考虑的下标位置
void dfs(int i) {
if (i > m - 1) {
// 该处根据题目来使用container
return;
}
// container在i处的所有可能的分支
for (int k = start; k < n; k++) {
container[i] = data[k];
start = k + 1;
dfs(i + 1);
// 回溯
container[i] = '\u0000';
start = k;
}
}
public static void main(String[] args) throws IOException {
new Main().run();
}
}