面试题38:字符串的排列
题目:输入一个字符串,打印出该字符串中字符的所有排列。例如,输入字符串abc,则打印出由字符 a、b、c 所能排列出来的所有字符串 abc、acb、bac、bca 和 cba。
思路:
- 把整个字符串的排列,看成两步:一、求出所有可能出现在第一个位置的字符,即把第一个字符和后面所有的字符交换。二、固定第一个字符,求后面所有字符的排列。
- 在求后面所有字符的排列的时候,也按照同样的方法来计算。
代码实现:
package Question38;
import java.util.HashSet;
import java.util.Set;
public class T01 {
static Set<String> strSet = new HashSet<>();
public static void main(String[] args) {
String str = "abc";
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder(str);
solve(sb1, sb2);
String[] result = new String[strSet.size()];
int index = 0;
for(String data : strSet) {
result[index++] = data;
System.out.println(data);
}
}
public static void solve(StringBuilder sb1, StringBuilder sb2) {
if(sb2.length() == 0) {
strSet.add(sb1.toString());
} else {
int len = sb2.length();
for(int i = 0; i < len; i++) {
char a = sb2.charAt(i);
sb1.append(sb2.charAt(i));
sb2.deleteCharAt(i);
solve(sb1, sb2);
sb1.deleteCharAt(sb1.length()-1);
sb2.insert(i, a);
}
}
}
}
本题扩展:
如果不是求字符的所有排列,而是求字符的所有组合,应该怎么办?还是三个字符,那他们的组合有 a、b、c、ab、ac、bc、abc。当交换字符串中的两个字符时,虽然能得到两个不同的排列,当却是同一个组合。比如 ab 和 ba 是不同的排列,但却是同一个组合。
思路:如果输入 n 个字符,则这 n 个字符能构成 1,2…n的组合。在求长度为 m 的组合的时候,可以先固定第一个字符,然后在剩下的字符中选取 m-1 个字符。这时又可以把它分为固定第一个字符,再剩下的字符中选取 m-2 个字符。因此,也可以通过递归实现。
代码实现:
package Question38;
import java.util.HashSet;
import java.util.Set;
public class T02 {
static Set<String> strSet = new HashSet<>();
public static void main(String[] args) {
String str = "abc";
int m = 1;
solve(str, m);
for(String data : strSet) {
System.out.println(data);
}
}
public static void solve(String data, int m) {
if(data == null || data.length() == 0) return ;
for(int i = 1; i <= m; i++) {
StringBuilder sb = new StringBuilder();
String str = data;
help(sb, str, i);
}
}
public static void help(StringBuilder sb, String str, int m) {
if(sb.length() == m) {
strSet.add(sb.toString());
} else {
int len = str.length();
for(int i = 0; i < len; i++) {
sb.append(str.charAt(i));
help(sb, str.substring(i+1), m);
sb.deleteCharAt(sb.length() - 1);
}
}
}
}
相关题目
1、输入一个含有 8 个数字的数组,判断有没有可能把这 8 个数字分别放到正方体的 8 个顶点上,使得正方体上三组相对的面上的 4 个顶点的和都相等。
思路:找出所有的排列,然后看是否满足条件 a1+a2+a3+a4=a5+a6+a7+a8 && a1+a2+a5+a6=a3+a4+a7+a8 && a1+a3+a5+a7=a2+a4+a6+a8。
代码实现:
package Question38;
import java.util.ArrayList;
import java.util.List;
public class T03 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
System.out.println(solve(arr));
}
public static boolean solve(int[] arr) {
if(arr == null || arr.length < 8) return false;
List<Integer> list2 = new ArrayList<>();
for(int data : arr) {
list2.add(data);
}
List<Integer> list1 = new ArrayList<>();
return help(list1, list2);
}
public static boolean help(List<Integer> list1, List<Integer> list2) {
if(list1.size() == 8) {
if(panduan(list1)) {
System.out.println(list1);
return true;
}
} else {
int len = list2.size();
for(int i = 0; i < len; i++) {
int num = list2.get(i);
list1.add(num);
list2.remove(i);
if(help(list1, list2)) return true;
list1.remove(list1.size() - 1);
list2.add(i, num);
}
}
return false;
}
public static boolean panduan(List<Integer> list) {
if(list.size() != 8) return false;
int a1 = list.get(0) +list.get(1) + list.get(2) + list.get(3);
int a2 = list.get(4) + list.get(5) + list.get(6) + list.get(7);
int a3 = list.get(0) + list.get(1) + list.get(4) + list.get(5);
int a4 = list.get(2) + list.get(3) + list.get(6) + list.get(7);
int a5 = list.get(0) + list.get(2) + list.get(4) + list.get(6);
int a6 = list.get(1) + list.get(3) + list.get(5) + list.get(7);
return (a1 == a2) && (a3 == a4) && (a5 == a6);
}
}
2、在 8×8 的国际象棋上摆放 8 个皇后,使其不能相互攻击,即任意两个皇后不得处于同一行、同一列、或者同一个对角线上。请问总共有多少种摆法?
思路:用一个数组表示八皇后拜访的位置。数组的下标代表皇后的行,值代表皇后拜访的列。所以,行肯定是不相同的,列取(1-8),所以也不会有两个及以上的皇后在同一列。在判断是否在同一斜线上时,可以用 arr[i] - arr[j] == i - j || arr[i] - arr[j] = j - i;
代码实现:
package Question38;
import java.util.ArrayList;
import java.util.List;
public class T04 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
System.out.println(solve(arr));
}
public static int solve(int[] arr) {
if(arr == null || arr.length != 8) return 0;
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
for(int data : arr) {
list2.add(data);
}
List<List<Integer>> result = new ArrayList<List<Integer>>();
help(list1, list2, result);
return result.size();
}
public static void help(List<Integer> list1, List<Integer> list2, List<List<Integer>> result) {
if(list1.size() == 8) {
result.add(list1);
} else {
int len = list2.size();
for(int i = 0; i < len; i++) {
int num = list2.get(i);
if(panduan(list1, num)) {
list1.add(num);
list2.remove(i);
help(list1, list2, result);
list1.remove(list1.size() - 1);
list2.add(i, num);
}
}
}
}
public static boolean panduan(List<Integer> list, int num) {
if(list.size() == 0) return true;
int len = list.size();
for(int i = 0; i < len; i++) {
if((num - list.get(i) == len - i) || (num - list.get(i) == i - len)) return false;
}
return true;
}
}
举一反三
如果面试题是按照一定要求摆放若干个数字,则可以先求出这些数字的所有排列,然后一一判断每个排列是不是满足题目给定的要求。