7.8全排列(上)
概念例题:输入一个字符串“abcd”,请输出它的所有字符的排列组合情况(字符串长度不变)。
实现源码:
import java.util.ArrayList;
public class 全排列例题1字符 {//迭代法
public static void main(String[] args) {
ArrayList<String> str = new 全排列例题1字符().get("abcd");
System.out.println(str);
}
public ArrayList<String> get(String A){
int n =A.length();
ArrayList<String> res = new ArrayList<String>();
res.add(A.charAt(0)+"");
for(int i = 1;i < n;i++) {//第i个字符
ArrayList<String> res_new = new ArrayList<String>();//放在外面的话就会导致无法刷新
char c = A.charAt(i);
for(String str : res) {//访问集合中的每一个字符串
res_new.add(c + str);//加到左边
res_new.add(str + c);//右边
for(int j = 1;j<str.length();j++) {//中间
res_new.add(str.substring(0,j) + c + str.substring(j));
}
}
res = res_new;//迭代,更新集合
}
return res;
}
}
输出结果:
7.9全排列(中)
在上一题的基础上,将字符串按首字母有序输出,即先输出第一位是A的字符串,再输出第一位是B的字符串,以此类推。
主要思想是通过回溯来对可能的结果进行搜索,到达最后一层时将结果存入集合中。
k=0时交换的是首字符(跟后面所有字符进行交换,即Axxx,Bxxx,Cxxx······),k=2时交换的是第二个字符,以此类推。
实现代码:
import java.util.ArrayList;
import java.util.Arrays;
public class 全排列例题2 {
public static void main(String[] args) {
ArrayList<String> str = new 全排列例题2().get("abcd");
System.out.println(str);
}
ArrayList<String> res = new ArrayList<String>();
public ArrayList<String> get(String A){
char[] arr = A.toCharArray();
Arrays.sort(arr);
getCore(arr,0);
return res;
}
public void getCore(char[] arr,int k ){
if(k == arr.length) {
res.add(new String(arr));//递归终止条件是到达最底层,k是层数
}
for(int i = k;i<arr.length;i++) {回溯到第k层后,k不增加i增加,相当于将k后面的数字都搜索一遍
swap(arr,i,k);//把后面的每个字符换到K位
getCore(arr,k+1);//深入一层,直到最底层
swap(arr,i,k);
}
}
public void swap(char[] arr,int i,int j) {//交换字符数组的元素
char temp;
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
7.9全排列(下)
题目(leetcode60):n个数的排列组合找出字典序的第K个排列
思路是通过前缀法来找出排列,
代码实现:
public class 全排列例题3 {
//题目(leetcode60):n个数的排列组合找出字典序的第K个排列
//用前缀法,从头开始扫描字符串,把可用的字符放在前缀的后面,更新前缀。
public static void main(String[] args) {
String arr = "abcd";
int k = 5;
get("",arr.toCharArray(),k);
System.out.println(arr);
}
static int i = 1;
public static void get(String pre,char[] arr,int k){//递归
if(pre.length() == arr.length) {
System.out.println(pre);
i++;
}
if(i>k) {
System.exit(0);
}
for(int j = 0;j<arr.length;j++) {
char ch = arr[j];
if(count(pre.toCharArray(),ch) < count(arr,ch)){
get(pre + ch,arr,k);
//如果在前缀中出现的次数小于在字符串中出现的次数
//那就说明这个arr[j]没出现过,那就可以加进去
}
}
}
public static int count(char[] arr,char ch){
int count = 0;
for(int i = 0; i<arr.length;i++){
if(ch == arr[i]){
count++;
}
}
return count;
}
}
总结:
上述题目中用到的迭代法、前缀法、回溯法其实核心都是一样的,即通过递归不断筛选并更新现有的集合。其中个人觉得最难得是回溯法,要回溯到原来的状态就必须设置一个变量用来表示当前这一层的状态,每一层都要向下搜索,下面的层搜索完之后会返回上一层。把文字写出来后又觉得,哎,这不就是普通的递归吗。。。只能说递归真是神奇。。。