java实现四种递归方法解决全排列问题(前缀法,插入法,交换法,选择法

java实现四种递归方法解决全排列问题(前缀法,插入法,交换法,选择法)

全排列问题

全排列是一个典型的递归问题,可以当作递归学习的入门问题。不了解什么是全排列问题的点我

说在前面

@Test 注解用于单元测试,读者不用的话改成main函数即可
这些代码是我准备蓝桥杯刷的一些题,这些方法名是我根据具体代码的特点命名,仅供参考

1.前缀法

过程举例
我们需要对strs = {“a”, “b”, “c”}全排列
如果数据很小,我们可以根据自己的经验就可以得到结果,abc,acb,bac,bca,cab,cba。但是要想让计算机完成,我们就要告诉他明确的步骤。
前缀法指的是,初始结果s="", 即当前 前缀为空,我们开始从头到尾扫描strs, 如果strs[i] 不在s字符串里面,那我们就把s加到strs[i]后面,可以得到

s1="a",  s2="b", s3="c" 。

这样我们就得到三个新的前缀.。
对于s1,我们再次从头到尾扫描strs, 如果strs[i] 不在s字符串里面,那我们就把s加到strs[i]后面,可以得到

s11="ab",  s12="ac"

对于s11,我们再次从头到尾扫描strs, 如果strs[i] 不在s字符串里面,那我们就把s加到strs[i]后面,可以得到

s111="abc" 此时字符串长度等于strs的长度,这就是我们想要的结果之一啦

程序实现如下

	static int acc20=0;
    @Test // 全排列 前缀法
    public void tset20() {
        String[] strs = {"a", "b", "c", "d", "e"};  // 全排列的数组 也可以是字符串,对此方法影响不大
        boolean[] jugs = new boolean[strs.length];  // 用于判断前缀相应位置是否存在对应字符
        fun20(strs, jugs, "");
        System.out.println(acc20);
    }
   /**
     * @param strs 全排列的数组
     * @param pro 存储前缀,最终结果的前一部分
     * @param jugs 用于判断前缀相应位置是否存在对应字符
     *             比如,pro="abcd" 那么jugs 只有jugs[4]=false,其他为true
     *
     */
    public static void fun20(String[] strs, boolean[] jugs, String pro) {
        if (pro.length() == strs.length) {
            System.out.println(pro); //此时字符串长度等于strs的长度 可以打印也可以存放到数组或者list容器中
            acc20++;
        }
        for (int i=0; i<strs.length; i++) {
            if (!jugs[i]) {
                jugs[i] = true;
                fun20(strs, jugs, pro+strs[i]);
                jugs[i] = false;  // 回溯 以免影响下次迭代 
            }
        }
    }

如果你对 jugs[i] = false; 语句产生疑问往下看
一个前缀字符串对应一个jugs判断数组,而且jugs 只有申请一次空间,所以所有的前缀共用同一个jugs
jugs[i]原本为false 如果你将jugs[i] = true;那么为了不影响下次别人使用需要将他还原

写博客很费时间 后面的三种方法,就请读者先按照strs = {“a”, “b”, “c”}或者数据更小的例子自己在演草纸上写一边过程,或者直接复制我的代码,然后一步一步调式。就行啦,这篇文章需要你对迭代有基础的了解,以后有时间我会写一遍迭代的文章。

插入法

  @Test // 全排列 递归插入法
    public void tset19() {
        String[] strs = {"a", "b", "c", "d", "e"};//, "c", "d", "e"};
        List<String> list = fun19(strs, strs.length-1);
        System.out.println();
    }
    /**
     * 
     * @param strs
     * @param n  表示当前考虑的插入元素为 strs[n] 
      * @return
     */
    public static List<String> fun19(String[] strs, int n) {
        List<String> list2 = new LinkedList<>(); // 存储返回结果
        if (n==0) {
            list2.add(strs[0]);
            return list2;
        }
        List<String> list1 = fun19(strs, n-1); // 获得上一级递归结果
        for (int i=0; i<list1.size(); i++) {
            String s = list1.get(i);
            list2.add(strs[n]+s); // 插在右边
            list2.add(s+strs[n]); // 插在左边
            for (int j=0; j<s.length()-1; j++) { // 循环插在某二个字符的中间
                String s2 = s.substring(0,j+1) + strs[n] + s.substring(j+1,s.length());        //      strs[n]
                list2.add(s2);
            }
        }
        return list2;
    }

交换法

这是我学会的第一种方法参考于阿珂

	@Test // 全排列 交换法
    public void tset018() {
        String[] strs = {"a", "b", "c", "d", "e"};//, "c", "d", "e"};
        fun18(strs, 0, strs.length);
    }

    /**
     *
     * @param strs
     * @param star  字面意思
     * @param end
     */
    public static void fun18(String[] strs, int star, int end) {
        if (star==end-1) {
            System.out.println(Arrays.toString(strs));
        }
        for (int i=star; i<end; i++) {
            swap18(strs, star, i);
            fun18(strs, star+1, end);
            swap18(strs, star, i);
        }
    }
    public static void swap18(String[] strs, int a, int b) {
        String tr = strs[a];
        strs[a] = strs[b];
        strs[b] = tr;
    }

选择法

	static int acc17=0;
    @Test // 全排列 选择法
    public void tset017() {
        String[] strs = {"a", "b", "c", "d", "e"};//, "c", "d", "e"};
        boolean[] jugs = new boolean[strs.length];
        fun17(strs, "", jugs, 0);
        System.out.println(acc17);
    }

    /**
     * @param strs 全排列的数组
     * @param s 存放结果
     * @param jugs  辅助
     * @param core  考虑当前元素下标
     */
    static void fun17(String[] strs, String s, boolean[] jugs, int core) {
        if (core == strs.length){
            System.out.println(s);
            acc17++;
            return;
        }
        for (int i=0; i< strs.length; i++) {
            if (!jugs[i]) {
                jugs[i] = true;
                fun17(strs, s+strs[i], jugs, core+1);
                jugs[i] = false;
            }
        }
    }

如果你看完这四种方法后,找到了其中的共同之处那么恭喜你,再遇见迭代就不慌了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值