字典序求字符串全排列:
要求 123456789 这个序列的全排列时,我们可以知道这个序列的取值范围
范围:123456789-987654321
如何进一步在这个范围内生成我们所需要的所有序列
思想:让连续的两个子序列的公共前缀尽可能的长
比如:当生成序列为 123456978,那么下一个序列是多少呢?当然是:123456987了
这样下来,就可以一个序列接着一个序列生成,最后生成所有的全排列的所有序列了
算法步骤 :
算法案例:
对于 346987521 下一个排列是什么?
从后向前找到第一个 arr[i] < arr[i+1],也就是递减位置,找到 6 的位置为递减的位置,因为 6 < 9 且 9 前面的都是递增的序列
扫描二维码关注公众号,回复:
8519254 查看本文章
再从后向前找到第一个 arr[j] > arr[i],也就是比 6 大的第一个数字,找到 7,因为 7 > 6 且 7 后面的都小于 6
将 7 和 6 调换位置得到序列:347986521
再将 7 所在位置的后缀(不包含 7)反转,从而得到:347125689
这样就得到了 346987521 的下一个排列:347125689
那么什么时候是结束呢?
当从后向前查找发现这个序列是一个递增序列的时候就结束了(因为已经到了生成序列的最大值了)
注意要点:要求一个字符串的全排列时,首先要对这个字符串内部字符进行排序,从而得到生成序列的最小值
算法实现代码:
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws Exception {
char[] arr = "abc".toCharArray();
Arrays.sort(arr);
while (permutationByDictionary(arr)) {}
}
private static void swap(int m, int n, char[] arr) {
char temp = arr[m];
arr[m] = arr[n];
arr[n] = temp;
}
private static void func(int index, char[] arr) {
if (index == arr.length - 1) {
System.out.println(new String(arr));
} else {
for (int i = index; i < arr.length; i++) {
swap(index, i, arr);
func(index + 1, arr);
swap(index, i, arr);
}
}
}
private static boolean permutationByDictionary(char[] arr) {
// 打印
System.out.println(Arrays.toString(arr));
// 从右向左找到第一个满足 arr[i] < arr[i+1] 的元素的下标 i
int i;
boolean isExist = false;
for (i = arr.length - 2; i >= 0; i--) {
if (arr[i] < arr[i + 1]) {
isExist = true;
break;
}
}
if (!isExist) {
return false;
}
int j;
// 从数组最后一个位置,从右向左找到第一个满足 arr[j] > arr[i] 的元素的下标 j
for (j = arr.length - 1; j > i; j--) {
if (arr[j] > arr[i]) {
break;
}
}
// 交换 arr[i] 和 arr[j]
swap(i, j, arr);
// 将下标为 i 后的所有元素进行反转操作,不包括(arr[i])
char[] temp = new char[arr.length];
int len = 0;
for (int k = i + 1; k < arr.length; k++) {
temp[len++] = arr[k];
}
for (int k = len - 1, n = i + 1; k >= 0; k--, n++) {
arr[n] = temp[k];
}
return true;
}
}
本篇就到这里了,如果有不清楚的地方,可以在下面评论,我会一一作出回复