模版 ----- 一维指数型枚举 & 排列型枚举 & 组合型枚举





一维指数型枚举

一、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();
    }
}


排列型枚举

应用场景

  1. 给定n个数,从中任意取m个(m <= n),枚举出所有情况(考虑顺序)

  2. 给定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(n1)+n(n1)(n2)+...+n!))),大概为 O ( n ∗ n ! ) O(n*n!) O(nn!)

代码模版

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();
    }
}

应用



组合型枚举

应用场景

  1. 给定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();
    }
}

应用

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值