输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
思路:回溯法
import java.util.ArrayList;
import java.util.Arrays;
public class LC47_String {
public ArrayList<String> Permutation(String str) {
ArrayList<String> res = new ArrayList<>();
if (str.length() == 0) {
return res;
}
char[] chars = str.toCharArray();
Arrays.sort(chars);
helper(res, new StringBuilder(), chars, new boolean[chars.length]);
return res;
}
private void helper(ArrayList<String> res, StringBuilder s, char[] chars, boolean[] used) {
if (s.length() == chars.length) {
res.add(s.toString());
return;
}
for (int i = 0; i < chars.length; i++) {
if (used[i]) {
continue;
}
if (i > 0 && chars[i] == chars[i - 1] && used[i - 1]) {
continue;
}
used[i] = true;
s.append(chars[i]);
helper(res, s, chars, used);
s.deleteCharAt(s.length() - 1);
used[i] = false;
}
}
public static void main(String[] args) {
LC47_String t = new LC47_String();
String str = "abc";
ArrayList<String> res = t.permute(str);
System.out.println(res);
}
}
思路一:字典序法(非递归)
【例】14763
直观的可以看出14763的所有排列中下一个比它大的最小排列是16347
通过字典序方法求解步骤是:
- 从右到左找到第一个 P(i) < P(i+1) 的位置,记为 firstSmall
- 从右到左找到第一个比 P(i) 大的数的位置,记为 firstLarge
- 交换 firstSmall 和 firstLarge 位置的值
- 把 firstSmall + 1 及它后面的数逆序
import java.util.ArrayList;
import java.util.Arrays;
public class T_27_Permutation {
public ArrayList<String> Permutation(String str) {
ArrayList<String> list = new ArrayList<>();
if (str.length() == 0) {
return list;
}
char[] chars = str.toCharArray();
Arrays.sort(chars); //将"bac"调整为"abc" ,需要按字典序打印,所以此时要sort
list.add(String.valueOf(chars));
int last = chars.length - 1;
while (true) {
//1.从右到左找到第一个P(i)<P(i+1)的位置,记为firstSmall
int firstSmall = -1;
for (int i = chars.length - 2; i >= 0; i--) {
if (chars[i] < chars[i + 1]) {
firstSmall = i;
break;
}
}
//每循环一次输出的是(按字典序)比当前数值大的下一种排序
//所以如果firstSmall == -1,表示当前已经是最大的了,没有下一种了,所以break
if (firstSmall == -1) {
break;
}
//2.从右到左找到第一个比P(i)大的数的位置,记为firstLarge
int firstLarge = -1;
for (int i = chars.length - 1; i >= 0; i--) {
if (chars[i] > chars[firstSmall]) {
firstLarge = i;
break;
}
}
//3.交换firstSmall和firstLarge位置的值
swap(chars, firstSmall, firstLarge);
//4.把 firstSmall + 1 及它后面的数逆序
reverse(chars, firstSmall + 1, chars.length - 1);
list.add(String.valueOf(chars));
}
return list;
}
private void swap(char[] chars, int i, int j) {
char temp;
temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
private void reverse(char[] chars, int i, int j) {
while (i < j) {
swap(chars, i++, j--);
}
}
}
思路二:递归法
- 第一步求所有可能出现在第一个位置的字符(即把第一个字符和后面的所有字符交换[相同字符不交换]);
- 第二步固定第一个字符,求后面所有字符的排列。这时候又可以把后面的所有字符拆成两部分(第一个字符以及剩下的所有字符),依此类推。
import java.util.ArrayList;
import java.util.Collections;
public class T_07_Permutation2 {
ArrayList<String> res = new ArrayList<>();
public ArrayList<String> Permutation(String str) {
if (str.length() == 0) {
return res;
}
PermutationHelper(str.toCharArray(), 0);
Collections.sort(res);
return res;
}
private void PermutationHelper(char[] chars, int i) {
if (i == chars.length - 1) {
res.add(String.valueOf(chars));
} else {
for (int j = i; j < chars.length; j++) {
if (j != i && chars[i] == chars[j]) {
continue;
}
swap(chars, i, j);
PermutationHelper(chars, i + 1);
swap(chars, i, j);
}
}
}
private void swap(char[] chars, int m, int n) {
char temp;
temp = chars[m];
chars[m] = chars[n];
chars[n] = temp;
}
}