JZ27 字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述: 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
思路:
对于一个长度为 nn 的字符串(假设字符互不重复),其排列方案数共有:n×(n−1)×(n−2)…×2×1
通过递归实现字符串的排列。在每一层递归中,根据当前未使用的字符,选择一个字符加入到当前排列中,并标记该字符为已使用。然后继续递归下一层,当排列的长度等于字符串长度时,将当前排列加入到结果中。最后,清除标记并回溯到上一层,继续尝试其他未使用的字符。
当字符串存在重复字符时,排列方案中也存在重复的排列方案。为排除重复方案,需在固定某位字符时,保证 “每种字符只在此位固定一次” ,即遇到重复字符时不交换,直接跳过。从 DFS 角度看,此操作称为 “剪枝” 。
func Permutation(str string) []string { result := []string{} visited := make([]bool, len(str)) strBytes := []byte(str) sort.Slice(strBytes, func(i, j int) bool {// 排序去除重复元素影响 return strBytes[i] < strBytes[j] }) backtrack(string(strBytes), []byte{}, visited, &result) return result } func backtrack(str string, path []byte, visited []bool, result *[]string) {// byte,bool是基础类型,会拷贝,所以不需要指针类型 if len(path) == len(str) { *result = append(*result, string(path)) return } for i := 0; i < len(str); i++ {// 这里要考虑重复元素的影响 if (visited)[i] || (i > 0 && str[i] == str[i - 1] && !(visited)[i-1]){ continue } visited[i] = true path = append(path, str[i]) backtrack(str, path, visited, result) visited[i] = false path = path[:len(path)-1] } }