最近在牛客网上刷题,遇到一道剑指offer上的编程题还蛮有意思的,所以把它记录下来与大家分享,原题如下:
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba
在网上查了以后发现这是一种特定的算法可以解决的问题,该算法称之为字典序全排列算法:
非递归算法
这种算法被用在了C++的STL库中。对给定的字符集中的字符规定了一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后。
一个全排列可看做一个字符串,字符串可有前缀、后缀。生成给定全排列的下一个排列.所谓一个的下一个就是这一个与下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。
要找到一个字符串的下一个应如下四个步骤:
【例】 如何得到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的下一个排列
由此思想不难想到字典序全排列算法的代码实现为:
import java.util.*;
public class Solution {
public static ArrayList<String> Permutation(String str) {
ArrayList<String> res = new ArrayList<>();
if (str != null && str.length() > 0) {
char[] seq = str.toCharArray();
Arrays.sort(seq); //排列
res.add(String.valueOf(seq)); //先输出一个解
int len = seq.length;
while (true) {
int p = len - 1, q;
//(1)从后向前找一个seq[p - 1] < seq[p]
while (p >= 1 && seq[p - 1] >= seq[p]) --p;
if (p == 0) break; //已经是“最小”的排列,退出
//(2)从p向后找最后一个比seq[p]大的数
q = p; --p;
while (q < len && seq[q] > seq[p]) q++;
--q;
//(3)交换这两个位置上的值
swap(seq, q, p);
//(4)将p之后的序列倒序排列
reverse(seq, p + 1);
res.add(String.valueOf(seq));
}
}
return res;
}
/*
public static void reverse(char[] seq, int start) {
int len;
if(seq == null || (len = seq.length) <= start)
return;
for (int i = 0; i < ((len - start) >> 1); i++) {
int p = start + i, q = len -1 - i;
if (p != q)
swap(seq, p, q);
}
}
*/
public static void reverse(char[] seq, int start){
int len = seq.length-1;
if(seq == null || len <= start)
return;
for(;start<len;start++,len--)
swap(seq,start,len);
}
public static void swap(char[] cs, int i, int j) {
char temp = cs[i];
cs[i] = cs[j];
cs[j] = temp;
}
public static void main(String[] args){
String str = "abcd";
ArrayList<String> array = Permutation(str);
for(int i=0;i<array.size();i++){
System.out.println(array.get(i));
}
}
}
输出为:
abcde
abced
abdce
abdec
abecd
abedc
acbde
acbed
acdbe
acdeb
acebd
acedb
adbce
adbec
adcbe
adceb
adebc
adecb
aebcd
aebdc
aecbd
aecdb
aedbc
aedcb
bacde
baced
badce
badec
baecd
baedc
bcade
bcaed
bcdae
bcdea
bcead
bceda
bdace
bdaec
bdcae
bdcea
bdeac
bdeca
beacd
beadc
becad
becda
bedac
bedca
cabde
cabed
cadbe
cadeb
caebd
caedb
cbade
cbaed
cbdae
cbdea
cbead
cbeda
cdabe
cdaeb
cdbae
cdbea
cdeab
cdeba
ceabd
ceadb
cebad
cebda
cedab
cedba
dabce
dabec
dacbe
daceb
daebc
daecb
dbace
dbaec
dbcae
dbcea
dbeac
dbeca
dcabe
dcaeb
dcbae
dcbea
dceab
dceba
deabc
deacb
debac
debca
decab
decba
eabcd
eabdc
eacbd
eacdb
eadbc
eadcb
ebacd
ebadc
ebcad
ebcda
ebdac
ebdca
ecabd
ecadb
ecbad
ecbda
ecdab
ecdba
edabc
edacb
edbac
edbca
edcab
edcba
递归法
import java.util.*;
public class zidianxu_digui {
public ArrayList<String> Permutation(String str) {
ArrayList<String> res = new ArrayList<>();
if (str != null && str.length() > 0) {
PermutationHelper(str.toCharArray(), 0, res);
Collections.sort(res);
}
return res;
}
private static void PermutationHelper(char[] cs, int i, ArrayList<String> list) {
if(i == cs.length - 1) { //解空间的一个叶节点
list.add(String.valueOf(cs)); //找到一个解
} else {
for(int j = i; j < cs.length; ++j) {
if(j == i || cs[j] != cs[i]) {
swap(cs, i, j);
PermutationHelper(cs, i + 1, list);
swap(cs, i, j); //复位
}
}
}
}
public static void swap(char[] cs, int i, int j) {
char temp = cs[i];
cs[i] = cs[j];
cs[j] = temp; }
public static void main(String[] args){
String str = "abcde";
zidianxu_digui text = new zidianxu_digui();
ArrayList<String> array = text.Permutation(str);
for(int i=0;i<array.size();i++){
System.out.println(array.get(i));
}
}
}