一、字符串的全排列
/**
* Created by april on 2018/8/2.
* 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
* 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
*/
public class String_PermutationList
{
/*
* 字典序法
* 123456789,最后一个是987654321
*
* 思想:从右到左若都是增的,也就没有下一个,否则找出第一次出现下降的位置
* 举例:
* 如何得到346987521的下一个
1,从尾部往前找第一个P(i-1) < P(i)的位置
3 4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1
最终找到6是第一个变小的数字,记录下6的位置i-1
2,从i位置往后找到最后一个大于6的数
3 4 6 -> 9 -> 8 -> 7 5 2 1
最终找到7的位置,记录位置为m
3,交换位置i-1和m的值
3 4 7 9 8 6 5 2 1
4,倒序i位置后的所有数据
3 4 7 1 2 5 6 8 9
则347125689为346987521的下一个排列
*/
public static ArrayList<String> PermutationList_1(String str){
ArrayList<String> list = new ArrayList<String>();
if(str==null || str.length()==0) return list;
//将字符串转为char数组,因为要将数组进行排序,并且数组易查找值
char[] chars = str.toCharArray();
Arrays.sort(chars);
//将char数组转为String添加进list,先输出第一个正序的
list.add(String.valueOf(chars));
int len = chars.length;
while(true){
int lessen_index = len-1;
int largen_index;
//从尾开始数过来第一个变小的字符
while(lessen_index>=1&&chars[lessen_index]<=chars[lessen_index-1]){
lessen_index--;
}
if(lessen_index==0) break;//
largen_index=lessen_index;
//求从lessen_index-1往后数的最后一个比它大的数
while(largen_index<len && chars[largen_index]>chars[lessen_index-1]){
largen_index++;
}
//交换lessen_index-1和largen_index
swap(chars, lessen_index-1, largen_index-1);
//从lessen_index开始到最后倒序置放
reverse(chars,lessen_index);
list.add(String.valueOf(chars));
}
return list;
}
public static void reverse(char[] chars,int lessen_index){
if(chars==null || chars.length<=lessen_index) return;
int len = chars.length;
for(int i=0;i<(len-lessen_index)/2;i++){
//第一个和最后一个开始交换,第二个和倒数第二个开始交换....
int front = lessen_index+i;
int end = len-1-i;
if(front<=end) swap(chars, front,end);
}
}
public static void swap(char[] chars, int i, int j){
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
/*递归求全排列,思想为回溯法
对于无重复值的情况
*
* 固定第一个字符,递归取得首位后面的各种字符串组合;
* 再把第一个字符与后面每一个字符交换,并同样递归获得首位后面的字符串组合; *递归的出口,就是只剩一个字符的时候,递归的循环过程,就是从每个子串的第二个字符开始依次与第一个字符交换,然后继续处理子串。
*
* 假如有重复值呢?
* *由于全排列就是从第一个数字起,每个数分别与它后面的数字交换,我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。
* 例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。
* 但是对bab,第二个数和第三个数不 同,则需要交换,得到bba。
* 由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。
*
* 换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,
* 所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!
* */
public ArrayList<String> PermutationList_2(String str){
ArrayList<String> result = new ArrayList<>();
if(str!=null && str.length()>0){
PerPermutationList_2_helper(str.toCharArray(),0,result);
Collections.sort(result);//排序
}
return result;
}
public void PerPermutationList_2_helper(char[] chars,int i,ArrayList<String> list){
//当为解空间的一个叶节点,找到一个解
if(i == chars.length-1) list.add(String.valueOf(chars));
else{
Set<Character> charSet = new HashSet<Character>();
for(int j = i;j<chars.length; ++j){
if(j == i || charSet.contains(chars[j])){
charSet.add(chars[j]);
swap(chars,j,i);
PerPermutationList_2_helper(chars,i+1,list);
swap(chars,j,i);//复位
}
}
}
}
public static void main(String[] args){
String str = new String("abc");
System.out.println(PermutationList_1(str));
}
}
二、可重复排列
public class String_PermutationList_Duplicate
{
/*可重复排列
1、第一个字符可从abc中选择一个,三种选择
2、abc组成长度为2的字符的情况
3、循环递归可以求出所有的可能
4、循环退出条件:当减到length==0,就输出字符串
* */
public static void per(char[] buf, String str,int length){
char[] chs = str.toCharArray();
if(length == 0){
for(int i = buf.length-1; i>=0; i--){
System.out.print(buf[i]);
}
System.out.println();
return;
}
for (int i = 0;i<chs.length;i++){
buf[length-1] = chs[i];
per(buf,str,length-1);
}
}
public static void main(String[] args){
String str = new String("abc");
per(new char[str.length()],str,str.length());
}
}
三、全组合
public class String_Comb
{
/*全组合,abc各个位是否选取,一共有2^n(0~2^3-1)
* abc 111,用0表示选取,用1表示不选取
* */
public static void comb(String str){
char[] chs = str.toCharArray();
int combNum = 1 << chs.length;//组合的个数有2^n的长度
int k;
for(int i = 0; i<combNum; i++){
for(int j = 0; j<chs.length; j++){
k = 1<<j;
if((k&i)!=0){//按位与运算,如果结果为1就输出当前位,结果为0不输出
System.out.print(chs[j]);
}
}
System.out.println();
}
}
public static void main(String[] args){
String str = new String("abc");
comb(str);
}
}