全排列的递归与非递归实现

1、全排列

将n个不同元素按照不同的顺序进行排列,一般要求所有的排列方式,或者满足某些要求的排列方式,比如先后顺序的限制

2、递归实现全排列

eg: 对[a b c d ]进行全排列,可以按照以下的步骤:
1. a后面加上[b c d]的全排列
2. b后面加上[a c d]的全排列
3. c后面加上[b a d]的全排列
4. d后面加上[b c a]的全排列
5. 对[b c d]等的处理和上面类似,即对每一个求全排列字符串的处理都是循环字符串中的每一个字符作为首字符,然后将剩余的部分作为一个完整的字符串再次对其求全排列,递归到底部就是当传入的参数只有一个字符串,此时输出,返回上一级的递归。
上代码:

/**
     * 递归实现全排列
     *
     * @param s     求全排列的字符串(有begin,end约束的子串)
     * @param begin
     * @param end
     */
    public static void premutation(String s, int begin, int end) {
        //递归结束条件:求全排列的字符串的长度为1
        if (begin == end) {
            System.out.println(s);
            return;
        }
        //对字符串的每一个字符进行循环,当做全排列的首字符
        for (int i = begin; i <= end; i++) {
            //将当前字符与首字符交换
            String sb = replaceCharacter(s, begin, i);
            //对剩下的子串再次进行全排列
            premutation(sb.toString(), begin + 1, end);
            //因为此处s并未改变,传入的参数是sb,所以无需对其进行还原
            //将前面交换的字符还原
            //s = replaceCharacter(sb.toString(), begin, i);
        }
    }

    public static String replaceCharacter(String s, int begin, int i) {
        char c = s.charAt(i);
        StringBuilder sb = new StringBuilder(s);
        sb.setCharAt(i, s.charAt(begin));
        sb.setCharAt(begin, c);
        return sb.toString();
    }
3、非递归实现全排列

即字典序全排列,基本思想是:先对需要求排列的字符串进行字典排序,记得到全排列中最小的排列,然后找到一个比它大的最小的全排列,一直重复这一步直到找到最大值(即字典排序的逆序列)
基本步骤是:

  1. 对字符串进行字典排序
  2. 找到一个最小的排列Ai
  3. 找到一个比Ai大的最小的后继排列Ai+1
  4. 重复上一步直到没有这样的后继

重点就是如何找到一个排列的直接后继。

  1. 对于字符串A0A1A2……An
  2. 从An到A0寻找第一次出现的升序排列的两个字符,即Ai< Ai+1,Ai+1是一个极值,因为Ai+1之后的字符串为降序排列,记top=i+1
  3. 从top处开始查找比Ai大的最小的值,记为temp
  4. 交换temp和i处的字符
  5. 倒置top之后的字符串(包括top),即得到了后继的排列

代码如下

 /**
     * 非递归实现全排列,字典序排列算法
     * @param s
     */
    public static void premutation2(String s){
        //1、首先对待求全排列的字符串进行字典序排序
        StringBuilder sb = sortStringByDict(s);
        System.out.println(sb);
        //2、检查是否此排列还有后继
        while(hasNext(sb)) {
            //1、从后往前查找第一个升序排列的字符的下标,即Ai < Ai+1, Ai+1也是一个极大值
            int top = 0;//top = i+1
            for (int i = sb.length() - 1; i > 0; i--) {
                if (sb.charAt(i) > sb.charAt(i - 1)) {
                    top = i;
                    break;
                }
            }
            int temp = top;
            //2、从top开始往后查找比Ai大的最小的值的下标,或者从后往前查找
            for (int i = sb.length() - 1; i >= top; i--) {
                if (sb.charAt(i) > sb.charAt(top - 1)) {
                    temp = i;
                    break;
                }
            }
            //3、交换temp和top-1
            sb = new StringBuilder(swapCharacter(sb.toString(), temp, top - 1));
            //4、从top往后倒置
            sb = new StringBuilder(sb.substring(0,top) + sb.reverse().substring(0, sb.length()-top));
            System.out.println(sb);
        }
    }

    public static boolean hasNext(StringBuilder sb) {
        boolean hasNext = false;
        for (int i = 1; i < sb.length(); i++) {
            if(sb.charAt(i) > sb.charAt(i-1)){
                hasNext = true;
                break;
            }
        }
        return hasNext;
    }

    public static StringBuilder sortStringByDict(String s) {
        StringBuilder sortStr = new StringBuilder(s);
        for (int i = 0; i < sortStr.length(); i++) {
            for (int j = i+1; j < sortStr.length(); j++) {
                if(sortStr.charAt(j) < sortStr.charAt(i)){
                    char c = sortStr.charAt(i);
                    sortStr.setCharAt(i, sortStr.charAt(j));
                    sortStr.setCharAt(j, c);
                }
            }
        }
        return sortStr;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值