一、递归算法
/
1、递归算法
解析:http://www.cnblogs.com/cxjchen/p/3932949.html (感谢该文作者!)
* 对于无重复值的情况
固定第一个字符,递归取得首位后面的各种字符串组合;
再把第一个字符与后面每一个字符交换,并同样递归获得首位后面的字符串组合; 递归的出口,就是只剩一个字符的时候,递归的循环过程,就是从每个子串的第二个字符开始依次与第一个字符交换,然后继续处理子串。
假如有重复值*呢?
由于全排列就是从第一个数字起,每个数分别与它后面的数字交换,我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。
例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。
但是对bab,第二个数和第三个数不 同,则需要交换,得到bba。
由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。
换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,
所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!
@param str
@return
/
代码
public class Solution {public static void main(String[] args){ArrayList<String> list = Permutation("abcd");for(int i = 0;i<list.size();i++){System.out.println(list.get(i));}}public static ArrayList<String> Permutation(String str){ArrayList<String> list = new ArrayList<String>();if(str!=null && str.length()>0){PermutationHelper(str.toCharArray(),0,list);Collections.sort(list);}return list;}public static void PermutationHelper(char[] ch,int p,ArrayList<String> list) {if(p==ch.length-1){list.add(String.valueOf(ch));}else{Set<Character> charSet = new HashSet<Character>();for(int i=p;i<ch.length;i++){//判断是否有重复的字符if(!charSet.contains(ch[i])){charSet.add(ch[i]);swap(ch,i,p);//把递归放到黑盒子里,不用思考它怎么运行的,理解调用递归的思路就可以PermutationHelper(ch, p+1, list);swap(ch,i,p);}}}}public static void swap(char[] ch,int i,int j){char temp;temp = ch[i];ch[i] = ch[j];ch[j] = temp;}}
二、字典序
代码
public 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;//从后向前找一个seq[p - 1] < seq[p]while (p >= 1 && seq[p - 1] >= seq[p]) --p;if (p == 0) break; //已经是“最小”的排列,退出//从p向后找最后一个比seq[p]大的数q = p; --p;while (q < len && seq[q] > seq[p]) q++;--q;//交换这两个位置上的值swap(seq, q, p);//将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 swap(char[] cs, int i, int j) {char temp = cs[i];cs[i] = cs[j];cs[j] = temp;}
三、递归法二
代码
import java.util.ArrayList;import java.util.List;import java.util.Collections;public class Solution {public ArrayList<String> Permutation(String str) {List<String> resultList = new ArrayList<>();if(str.length() == 0)return (ArrayList)resultList;//递归的初始值为(str数组,空的list,初始下标0)fun(str.toCharArray(),resultList,0);Collections.sort(resultList);return (ArrayList)resultList;}private void fun(char[] ch,List<String> list,int i){//这是递归的终止条件,就是i下标已经移到char数组的末尾的时候,考虑添加这一组字符串进入结果集中if(i == ch.length-1){//判断一下是否重复if(!list.contains(new String(ch))){list.add(new String(ch));return;}}else{//这一段就是回溯法,这里以"abc"为例//递归的思想与栈的入栈和出栈是一样的,某一个状态遇到return结束了之后,会回到被调用的地方继续执行//1.第一次进到这里是ch=['a','b','c'],list=[],i=0,我称为 状态A ,即初始状态//那么j=0,swap(ch,0,0),就是['a','b','c'],进入递归,自己调自己,只是i为1,交换(0,0)位置之后的状态我称为 状态B//i不等于2,来到这里,j=1,执行第一个swap(ch,1,1),这个状态我称为 状态C1 ,再进入fun函数,此时标记为T1,i为2,那么这时就进入上一个if,将"abc"放进list中/////////////-------》此时结果集为["abc"]//2.执行完list.add之后,遇到return,回退到T1处,接下来执行第二个swap(ch,1,1),状态C1又恢复为状态B//恢复完之后,继续执行for循环,此时j=2,那么swap(ch,1,2),得到"acb",这个状态我称为C2,然后执行fun,此时标记为T2,发现i+1=2,所以也被添加进结果集,此时return回退到T2处往下执行/////////////-------》此时结果集为["abc","acb"]//然后执行第二个swap(ch,1,2),状态C2回归状态B,然后状态B的for循环退出回到状态A// a|b|c(状态A)// |// |swap(0,0)// |// a|b|c(状态B)// / \// swap(1,1)/ \swap(1,2) (状态C1和状态C2)// / \// a|b|c a|c|b//3.回到状态A之后,继续for循环,j=1,即swap(ch,0,1),即"bac",这个状态可以再次叫做状态A,下面的步骤同上/////////////-------》此时结果集为["abc","acb","bac","bca"]// a|b|c(状态A)// |// |swap(0,1)// |// b|a|c(状态B)// / \// swap(1,1)/ \swap(1,2) (状态C1和状态C2)// / \// b|a|c b|c|a//4.再继续for循环,j=2,即swap(ch,0,2),即"cab",这个状态可以再次叫做状态A,下面的步骤同上/////////////-------》此时结果集为["abc","acb","bac","bca","cab","cba"]// a|b|c(状态A)// |// |swap(0,2)// |// c|b|a(状态B)// / \// swap(1,1)/ \swap(1,2) (状态C1和状态C2)// / \// c|b|a c|a|b//5.最后退出for循环,结束。for(int j=i;j<ch.length;j++){swap(ch,i,j);fun(ch,list,i+1);swap(ch,i,j);}}}//交换数组的两个下标的元素private void swap(char[] str, int i, int j) {if (i != j) {char t = str[i];str[i] = str[j];str[j] = t;}}}
四、回溯法
代码
public class Solution {private static ArrayList<String> res = new ArrayList<>();private static TreeSet<String> paths = new TreeSet<>();private static StringBuilder path = new StringBuilder();private static boolean[] visited;public static void main(String[] ags){ArrayList<String> list = Permutation("abc");for(int i = 0;i<list.size();i++) {System.out.println(list.get(i));}}public static ArrayList<String> Permutation(String str) {if (str == null || str.equals("")) {return res;}char[] strs = str.toCharArray();Arrays.sort(strs);visited = new boolean[strs.length];combination(strs, 0);res.addAll(paths);return res;}private static void combination(char[] strs, int len) {if (len == strs.length) {paths.add(path.toString());return;}for (int i = 0; i < strs.length; i++) {if (!visited[i]) {visited[i] = true;path.append(strs[i]);combination(strs, len + 1);//Duang ~ 回溯 - 状态重置visited[i] = false;path.deleteCharAt(path.length() - 1);}}}}
