剑指offer_字符串的排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc,则打印出由字符 a, b, c 所能排 列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。
使用回溯法解决
1 import java.util.*; 2 3 public class Solution { 4 5 private ArrayList<String> ret = new ArrayList<>(); 6 7 public ArrayList<String> Permutation(String str) { 8 if (str.length() == 0) 9 return ret; 10 char[] chars = str.toCharArray(); 11 Arrays.sort(chars); 12 backtracking(chars, new boolean[chars.length], new StringBuilder()); 13 return ret; 14 } 15 16 private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s) { 17 System.out.println(s); 18 if (s.length() == chars.length) { 19 ret.add(s.toString()); 20 return; 21 } 22 for (int i = 0; i < chars.length; i++) { 23 if (hasUsed[i]) 24 continue; 25 if (i != 0 && chars[i] == chars[i - 1] && !hasUsed[i - 1]) /* 保证不重复 */ 26 continue; 27 hasUsed[i] = true; 28 29 s.append(chars[i]); 30 backtracking(chars, hasUsed, s); 31 s.deleteCharAt(s.length() - 1); 32 hasUsed[i] = false; 33 } 34 } 35 }
结合ABC这个例子具体描述一下算法流程:
1) 在第一层,A是第一个未访问的元素,将A添加到输出队列,同时将A标记为红色(已访问);当前层level=1,不是最后一层,递归到第二层;
2) 在第二层,因为A已经访问过,所以B是第一个未访问过的元素,将B添加到输出队列,同时将B标记为红色(已访问),当前层level=2,不是最后一层,递归到第三层
3)在第三层,因为A,B都已经访问过,所以C是第一个未访问过的元素,将C添加到输出队列,同时将C标记为红色(已访问),当前层level=3,是最后一层;到达最后一层之后说明找到了一个排列ABC,将该排列输出;到达最后一层之后需要向前一层(第二层)回溯,在回溯之前需要将C标记为未访问,因为C是在当前层(第三层)访问的,而上一层(第二层)并没有访问,因此在回溯到第二层之前各元素的访问状态应该恢复到第二层的初始状态;
4)回溯到第二层之后,寻找第二个未访问过元素C,将C添加到输出队列,(注意:这里的C将会覆盖在第 2步中添加到队列的B,因为输出队列中元素的下标是level),同时将C标记为红色(已访问),当前层level=2,不是最后一层,递归到第三层。
5) 此时的第三层第一个未标记的元素是B,将B添加到输出队列,同时将B标记为红色(已访问),当前层为第三层是最后一层;到达最后一层之后说明又找到一个排列ACB,将该排列输出;接着再回溯到第二层,回溯到第二层之前,同样需要将C标记为未访问。
6)此时又回溯到第二层,第二层的第二,第三个元素都已经遍历过,即第二层此时不存在未遍历过的元素,此时需要回溯到第一层,同理,回溯到第一层直前,需要将A标记为未访问。
7) 此时回溯到第一层,寻找第二个未访问过的元素B,将B添加到输出队列,同时标记B为红色(已访问),下面的步骤和第2步中的做法一致。
8)到最后输出了每一个可能的排列。

浙公网安备 33010602011771号