字符串的排列

来源:互联网 发布:2018域名 编辑:程序博客网 时间:2024/06/07 01:39

剑指Offer第28题

题目

输入一个字符串,打印出该字符串中字符的所有排列,例如输入abc,则打印出由字符abc,acb,bac,bca,cab,cba。

思路:

我们可以把复杂的问题拆分成小问题:

  首先:确定第一个字符a,然后剩下后面两个bc,bc在进行排列

  递归深入:首先确定第一个字符b,然后剩下的c进行排列

     递归深入:c是最后一个字符,输出。

    递归返回:交换bc,再次递归........

依次这样下去,当返回到第一个字符时,又再次把a和b一交换即可。

代码如下:

<span style="white-space:pre"></span>public static void Permutation(String str){if(str == null || str.length()==0 )return ; char[] arr = str.toCharArray();truePermutation(arr,0);}private static void truePermutation(char[] arr,int i) {if(i == arr.length - 1){System.out.println(arr);}for(int j=i;j<arr.length;j++){//交换好第一个和后面的, char temp = arr[j]; arr[j] = arr[i]; arr[i] = temp;  //直接递归后面的就可以 truePermutation(arr,i+1);  //交换回来,到第一次还没交换的时候 temp = arr[j]; arr[j] = arr[i]; arr[i] = temp;}}

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

延伸1:

关于这道题可以延伸,上面的是没有出现重复字符,那如果出现重复字符会怎么样?上面的肯定不会合适的。

这时候,我们只需要在处理前加一个判断,我们判断最开始的那一个字符和当前字符之间有没有重复的字符。

也就是说:当输入是 aac 的时候,我们判断第一个a和第二个a中间有相等的(把第二个a看成中间的),就不交换了。

所以我们每次交换的是字符1 和第一个不重复的字符2。

代码如下:

/** *  * 思路: * 如果i与j之间,还有与j相同的,那就不交换了,说明有重复值 * 只需要将i与重复值的第一个交换即可 *  * 去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换 * */  public static void sequenceRepeatString(String str){if(str ==null || str.length()==0)return ;char [] arr = str.toCharArray();truesequenceRepeatString(arr,0);}private static void truesequenceRepeatString(char[] arr,int i) {  if(i == arr.length - 1){System.out.println(arr);}for(int j=i;j<arr.length;j++){ //在这里加入判断  if(isSwap(arr,i,j)) {//交换好第一个和后面的, char temp = arr[j]; arr[j] = arr[i]; arr[i] = temp;   //直接递归后面的就可以 truesequenceRepeatString(arr,i+1);  //交换回来,到第一次还没交换的时候  temp = arr[j]; arr[j] = arr[i]; arr[i] = temp; }   }}private static boolean isSwap(char[] arr, int i, int j) {for(int k = i;k<j ;k++){if(arr[k] == arr[j])return false;}return true;}

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

课后题:

剑指offer的后面提到,如果要求一个字符串的组合,abc的组合就是:a,b,c,ab,ac,bc,abc。ab和ba是一个组合。
这也就是所谓的排列组合。

思路:

当我们要选择长度为n的字符时,每次碰到一个字符的时候,要么将这个字符放入到所要选的集合中,在后面的字符中选n-1个字符;
要么就不放入这个字符,在后面的字符中选n个字符。


代码如下:

/** * 字符串的全组合,字符串内无重复重复 * abc: * a,b,c,ab,ac,abc * 像ab,bc是一个组合 *  * */public static void combineString(String str){if(str == null)return;                //定义一个栈保存我们所要选的序列Stack<Character> st = new Stack<Character>();for(int i =1 ;i<=str.length();i++)trueCombineString(str.toCharArray(),i,0,st);}private static void trueCombineString(char[] arr,int m,int index, Stack<Character> st){if(arr == null )return;if(index==arr.length || m==0){if(m==0)              //m不等于0,就说明后面的字符总长度加起来都不等于所要选择的长度。    System.out.println(st.toString());return ;}//如果选第一个st.push(arr[index]);trueCombineString(arr,m-1,index+1, st);st.pop();//不选第一个trueCombineString(arr,m,index+1, st);}

在网上看到一段非常好的解法:利用位运算。
也就是说:我们平时看到的二进制:001 010 011 100 101.....这些都是无重复的排列组合。可以利用这个特性,将abc对应的位置输出。
  比如说001,就输出c,011,输出bc,111,输出abc。
/** *  * 位运算求组合 *  *  * */public static void bitCombine(String str){for(int i=1;i<(1<<str.length());++i) //比如"abc",i就必须小于1000(二进制),这样后面三位才可以判断trueBitCombine(str,i);}private static void trueBitCombine(String str, int i) {for(int k=0;k<str.length();k++){//System.out.println((i & (1<<k))+".....");if((i & (1<<k)) !=0)  //一次判断i的那一位是1,是就输出System.out.print(str.charAt(k)+" ");}System.out.println();}

当然如果组合中带重复值,我们还需要判断一下(针对第一种解法):
思路:
利用一个集合保存我们已经输出过的值,当再次输出的时候,判断集合中有没有该元素,如果有,就不用输出。
代码就不上了。


0 0
原创粉丝点击