一、递归算法

/
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
/

代码

  1. public class Solution {
  2. public static void main(String[] args)
  3. {
  4. ArrayList<String> list = Permutation("abcd");
  5. for(int i = 0;i<list.size();i++)
  6. {
  7. System.out.println(list.get(i));
  8. }
  9. }
  10. public static ArrayList<String> Permutation(String str){
  11. ArrayList<String> list = new ArrayList<String>();
  12. if(str!=null && str.length()>0){
  13. PermutationHelper(str.toCharArray(),0,list);
  14. Collections.sort(list);
  15. }
  16. return list;
  17. }
  18. public static void PermutationHelper(char[] ch,int p,ArrayList<String> list) {
  19. if(p==ch.length-1)
  20. {
  21. list.add(String.valueOf(ch));
  22. }else
  23. {
  24. Set<Character> charSet = new HashSet<Character>();
  25. for(int i=p;i<ch.length;i++)
  26. {
  27. //判断是否有重复的字符
  28. if(!charSet.contains(ch[i]))
  29. {
  30. charSet.add(ch[i]);
  31. swap(ch,i,p);//把递归放到黑盒子里,不用思考它怎么运行的,理解调用递归的思路就可以
  32. PermutationHelper(ch, p+1, list);
  33. swap(ch,i,p);
  34. }
  35. }
  36. }
  37. }
  38. public static void swap(char[] ch,int i,int j)
  39. {
  40. char temp;
  41. temp = ch[i];
  42. ch[i] = ch[j];
  43. ch[j] = temp;
  44. }
  45. }

二、字典序

代码

  1. public ArrayList<String> Permutation(String str) {
  2. ArrayList<String> res = new ArrayList<>();
  3. if (str != null && str.length() > 0) {
  4. char[] seq = str.toCharArray();
  5. Arrays.sort(seq); //排列
  6. res.add(String.valueOf(seq)); //先输出一个解
  7. int len = seq.length;
  8. while (true) {
  9. int p = len - 1, q;
  10. //从后向前找一个seq[p - 1] < seq[p]
  11. while (p >= 1 && seq[p - 1] >= seq[p]) --p;
  12. if (p == 0) break; //已经是“最小”的排列,退出
  13. //从p向后找最后一个比seq[p]大的数
  14. q = p; --p;
  15. while (q < len && seq[q] > seq[p]) q++;
  16. --q;
  17. //交换这两个位置上的值
  18. swap(seq, q, p);
  19. //将p之后的序列倒序排列
  20. reverse(seq, p + 1);
  21. res.add(String.valueOf(seq));
  22. }
  23. }
  24. return res;
  25. }
  26. public static void reverse(char[] seq, int start) {
  27. int len;
  28. if(seq == null || (len = seq.length) <= start)
  29. return;
  30. for (int i = 0; i < ((len - start) >> 1); i++) {
  31. int p = start + i, q = len - 1 - i;
  32. if (p != q)
  33. swap(seq, p, q);
  34. }
  35. }
  36. public static void swap(char[] cs, int i, int j) {
  37. char temp = cs[i];
  38. cs[i] = cs[j];
  39. cs[j] = temp;
  40. }

三、递归法二

代码

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.Collections;
  4. public class Solution {
  5. public ArrayList<String> Permutation(String str) {
  6. List<String> resultList = new ArrayList<>();
  7. if(str.length() == 0)
  8. return (ArrayList)resultList;
  9. //递归的初始值为(str数组,空的list,初始下标0)
  10. fun(str.toCharArray(),resultList,0);
  11. Collections.sort(resultList);
  12. return (ArrayList)resultList;
  13. }
  14. private void fun(char[] ch,List<String> list,int i){
  15. //这是递归的终止条件,就是i下标已经移到char数组的末尾的时候,考虑添加这一组字符串进入结果集中
  16. if(i == ch.length-1){
  17. //判断一下是否重复
  18. if(!list.contains(new String(ch))){
  19. list.add(new String(ch));
  20. return;
  21. }
  22. }else{
  23. //这一段就是回溯法,这里以"abc"为例
  24. //递归的思想与栈的入栈和出栈是一样的,某一个状态遇到return结束了之后,会回到被调用的地方继续执行
  25. //1.第一次进到这里是ch=['a','b','c'],list=[],i=0,我称为 状态A ,即初始状态
  26. //那么j=0,swap(ch,0,0),就是['a','b','c'],进入递归,自己调自己,只是i为1,交换(0,0)位置之后的状态我称为 状态B
  27. //i不等于2,来到这里,j=1,执行第一个swap(ch,1,1),这个状态我称为 状态C1 ,再进入fun函数,此时标记为T1,i为2,那么这时就进入上一个if,将"abc"放进list中
  28. /////////////-------》此时结果集为["abc"]
  29. //2.执行完list.add之后,遇到return,回退到T1处,接下来执行第二个swap(ch,1,1),状态C1又恢复为状态B
  30. //恢复完之后,继续执行for循环,此时j=2,那么swap(ch,1,2),得到"acb",这个状态我称为C2,然后执行fun,此时标记为T2,发现i+1=2,所以也被添加进结果集,此时return回退到T2处往下执行
  31. /////////////-------》此时结果集为["abc","acb"]
  32. //然后执行第二个swap(ch,1,2),状态C2回归状态B,然后状态B的for循环退出回到状态A
  33. // a|b|c(状态A)
  34. // |
  35. // |swap(0,0)
  36. // |
  37. // a|b|c(状态B)
  38. // / \
  39. // swap(1,1)/ \swap(1,2) (状态C1和状态C2)
  40. // / \
  41. // a|b|c a|c|b
  42. //3.回到状态A之后,继续for循环,j=1,即swap(ch,0,1),即"bac",这个状态可以再次叫做状态A,下面的步骤同上
  43. /////////////-------》此时结果集为["abc","acb","bac","bca"]
  44. // a|b|c(状态A)
  45. // |
  46. // |swap(0,1)
  47. // |
  48. // b|a|c(状态B)
  49. // / \
  50. // swap(1,1)/ \swap(1,2) (状态C1和状态C2)
  51. // / \
  52. // b|a|c b|c|a
  53. //4.再继续for循环,j=2,即swap(ch,0,2),即"cab",这个状态可以再次叫做状态A,下面的步骤同上
  54. /////////////-------》此时结果集为["abc","acb","bac","bca","cab","cba"]
  55. // a|b|c(状态A)
  56. // |
  57. // |swap(0,2)
  58. // |
  59. // c|b|a(状态B)
  60. // / \
  61. // swap(1,1)/ \swap(1,2) (状态C1和状态C2)
  62. // / \
  63. // c|b|a c|a|b
  64. //5.最后退出for循环,结束。
  65. for(int j=i;j<ch.length;j++){
  66. swap(ch,i,j);
  67. fun(ch,list,i+1);
  68. swap(ch,i,j);
  69. }
  70. }
  71. }
  72. //交换数组的两个下标的元素
  73. private void swap(char[] str, int i, int j) {
  74. if (i != j) {
  75. char t = str[i];
  76. str[i] = str[j];
  77. str[j] = t;
  78. }
  79. }
  80. }

四、回溯法

代码

  1. public class Solution {
  2. private static ArrayList<String> res = new ArrayList<>();
  3. private static TreeSet<String> paths = new TreeSet<>();
  4. private static StringBuilder path = new StringBuilder();
  5. private static boolean[] visited;
  6. public static void main(String[] ags)
  7. {
  8. ArrayList<String> list = Permutation("abc");
  9. for(int i = 0;i<list.size();i++) {
  10. System.out.println(list.get(i));
  11. }
  12. }
  13. public static ArrayList<String> Permutation(String str) {
  14. if (str == null || str.equals("")) {
  15. return res;
  16. }
  17. char[] strs = str.toCharArray();
  18. Arrays.sort(strs);
  19. visited = new boolean[strs.length];
  20. combination(strs, 0);
  21. res.addAll(paths);
  22. return res;
  23. }
  24. private static void combination(char[] strs, int len) {
  25. if (len == strs.length) {
  26. paths.add(path.toString());
  27. return;
  28. }
  29. for (int i = 0; i < strs.length; i++) {
  30. if (!visited[i]) {
  31. visited[i] = true;
  32. path.append(strs[i]);
  33. combination(strs, len + 1);
  34. //Duang ~ 回溯 - 状态重置
  35. visited[i] = false;
  36. path.deleteCharAt(path.length() - 1);
  37. }
  38. }
  39. }
  40. }