字典序法求全排列

字符串的排列

http://www.nowcoder.com/questionTerminal/fe6b651b66ae47d7acce78ffdd9a96c7

就当一个笔记嘻嘻

来来来先看一下算法的过程
给定一个字符串str,我们可以按照如下步骤找到比该字符串str大一位的下一个排列合租
1、从右向左找开始下降的位置,即第一次出现i-1 < i的位置索引 i
1.1、找到头也没有这样的情况时候,结束,说明该组合是最大的情况了
2、从i-1 的位置开始向右找 最后一个比str[i-1]大的数max,该数max的索引位置 j
3、交换 str[i-1] 和 str[j] 的值
4、把i-1之后的位置(不包括i-1)的值进行翻转,即得到下一个较大的排列组合

来想一下为什么这么做可行呢

我们是要找下一个大的对不对,所以找到前面小的数lit 和 后面 比该数lit 大, 并且是大中的 最小的那个数lar,然后交换他们,就相当于把后面最大的,但又是最小的那个数放到前面,把前面的小数放到后面去,大的数放到前面来,按照字典序,自然变大了,接下来再进行一个翻转,又把后面的较大的数放到了后面去,小的又靠前了。

如:
str=2143
1、2 > 1 < 4 > 3,1 < 4,所以在i = 2的时候开始下降,i-1 = 1
1.1、还没有结束
2、最后一个 比str[i-1]=1 大 的数 是 str[3] = 3
3、交换1(str[i-1]) 和 3 (str[3]) ---> 2341
4、倒置str[i-1]后面的位置--->2314

看一个重复的情况:str=aab
aab i=2 i-1=1
str[i-1] =a < str[2] = b
交换i-1和2:aba
倒置i-1=1之后的:aba (i-1后面就一位)

aba i=1, i-1 = 0
str[i-1] = a < str[1] = b
交换i-1和1:baa
倒置i-1=0之后的:baa

aab aba baa

该算法的好处:1、能够到达去重效果,2、能够预知下一个排列组合,3、时间复杂度相对低,并且能够达到1和2的效果

import com.alibaba.fastjson.JSON;

import java.util.ArrayList;

public class Solution {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        Solution test = new Solution();
        ArrayList<String> abc = test.Permutation("123456789");
        System.out.println(System.currentTimeMillis() - start);
        System.out.println(JSON.toJSONString(abc));
    }

    public ArrayList<String> Permutation(String str) {
        ArrayList<String> result = new ArrayList<String>();
        if(str == null || str.length() == 0) return result;
        result.add(str);
        char[] chars = str.toCharArray();
        while (true) {
            int i = findPreMin(chars);
            if(i == -1) {
                break;
            }
            int j = findForMax(chars, i);
            swap(chars, i - 1, j);
            inverse(chars, i);
            result.add(String.valueOf(chars));
        }
        return result;
    }

    /**
     * 翻转指定位置后面部分
     * @param str
     * @param index
     * @return
     */
    public void inverse(char[] str, int index) {
        int i=index, j=str.length - 1;
        while (true) {
            // 奇数个
            if(i == j) {
                break;
            }
            // 偶数个
            if(j - i == 1) {
                swap(str, i, j);
                break;
            }
            swap(str, i, j);
            i++;
            j--;
        }
    }

    /**
     * 交换 指定两个位置 的值
     * @param str
     * @param index1
     * @param index2
     * @return
     */
    public void swap(char[] str, int index1, int index2) {
        char tmp1 = str[index1];
        str[index1] = str[index2];
        str[index2] = tmp1;
    }

    /**
     * 向左找到下降点的坐标
     * @param str
     * @return
     */
    public int findPreMin(char[] str) {
        int length = str.length;
        for(int i=length-1; i>0; i--) {
            if(str[i-1] < str[i]) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 向右找到 比 指定位置index 大的 最小的 那个数的坐标
     * @param str
     * @param index
     * @return
     */
    public int findForMax(char[] str, int index) {
        /*for(int i=index; i<str.length; i++) {
            if(str[index] < str[i] && str[index] > str[i+1]) {
                return i;
            }
        }
        return str.length-1;*/
        int minMaxIndex = index;
        while(minMaxIndex < str.length && str[minMaxIndex] > str[index-1]) {
            minMaxIndex ++;
        }
        return minMaxIndex - 1;
    }
}
全部评论

相关推荐

点赞 评论 收藏
分享
11-18 09:44
Java
小白也想要offer:简历别放洋屁,搞不还还放错了,当然你投外企除外,以上纯属个人观点
点赞 评论 收藏
分享
评论
1
1
分享
牛客网
牛客企业服务