我想让函数以字典顺序从输入字符串输出所有可能的排列。
我有以下代码:
{{1}}
输入字符串排列是permutations_print已排序。
对于只有独特字符的排列,它没有任何问题,但是我还需要它来处理字符非唯一字符串,任何想法如何调整/修改/工作?我必须使用递归,不应该使用任何类型的排序,我不应该将它存储在任何数组中,只需打印。我想打印所有,甚至重复。
答案 0 :(得分:0)
警告:不是C程序员,所以我的代码肯定可以改进。
您可以使用以下内容迭代执行:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void swap(char* array, int i, int j);
void reverse(char* array, int left, int right);
bool nextPermutation(char* array, int n);
int compareCharacters(const void * a, const void * b);
void printPermutations(char *permutations, int length);
int main() {
char myArray[] = "hey";
printPermutations(myArray, 3);
return 0;
}
void printPermutations(char *array, int length) {
qsort(array, length, sizeof(char), compareCharacters);
*(array + length) = '\0';
do {
printf("%s\n", array);
} while (nextPermutation(array, length));
}
int compareCharacters(const void * a, const void * b) {
return (*(char*)a - *(char*)b);
}
bool nextPermutation(char* array, int n) {
if (n <= 1) {
return false;
}
// Find index, swapIndex1, of rightmost number that has a number greater than it
int swapIndex1;
for (swapIndex1 = n - 2; swapIndex1 >= 0; --swapIndex1) {
if (array[swapIndex1] < array[swapIndex1 + 1]) {
break;
}
}
if (swapIndex1 == -1) {
return false;
}
// Find index, swapIndex2, of smallest number to the right of that and greater than it
int swapIndex2 = swapIndex1 + 1;
int minToRight = array[swapIndex2];
for (int i = swapIndex2 + 1; i < n; ++i) {
if (array[i] <= minToRight && array[i] > array[swapIndex1]) {
minToRight = array[i];
swapIndex2 = i;
}
}
// Swap values at swapIndex1 and swapIndex2
swap(array, swapIndex1, swapIndex2);
// Reverse values from swapIndex1+1 to n-1
reverse(array, swapIndex1 + 1, n - 1);
return true;
}
void swap(char* array, int i, int j) {
char temp = array[i];
array[i] = array[j];
array[j] = temp;
}
void reverse(char* array, int left, int right) {
for (; left < right; ++left, --right) {
swap(array, left, right);
}
}
该算法被描述为here。有关示例/解释,请参阅注释部分,或参见下面的转录:
我们假设我们正在寻找1-9中下一个最大的数字排列。
例如,假设我们有:123479865
我们希望找到大于123479865的最小数字,可以通过重新排列123479865的数字来获得。
这意味着我们必须至少比现在大一个数字。如果我们有自己的选择,我们会希望将最右边的数字放大,因为这会导致最小的变化。如果我们要使左数字更大,则会导致更大的值变化。
例如:12347986 5 =&gt; 12347986 6 的变化小于 1 23479865 =&gt;的 2 强> 23479865
因此,我们希望找到我们可以做得更大的最右边的数字。为了使数字更大,我们必须在序列中找到比它更大的另一个数字然后交换这两个数字。
注意:我们不能将数字(例如5)与左边的数字交换(例如,6),因为那时我们将减少左边数字的值,这将使我们的整体数字变小。例如,如果我们将5与6交换,我们将获得1234798 56 ,这小于1234798 65 。因此,我们总是与我们的权利交换一个数字。
所以让我们通过123479865,从最右边的数字开始。
5的右边没有大于5的东西,所以我们不能让5更大。
现在让我们考虑一下6.在6的右边没有大于6的东西,所以我们不能让6更大。
现在让我们考虑8.在8的右边没有大于8的东西,所以我们不能让8更大。
现在让我们考虑一下9.在9的右边没有大于9的内容,所以我们不能让9更大。
现在让我们考虑一下7.在7的右边有一些大于7的数字,即:9和8.因此,我们可以使7更大。我们希望以最小的数量使它变大,所以我们应该将它与大于7的最小值交换。换句话说,我们应该将7与8交换。这给了我们:1234 8 9的 7 强> 65
数字123489765大于123479865,但我们实际上可以将它缩小,同时保持它大于123479865.我们可以这样做,因为我们现在有无限的自由来改变以下任何数字:12348 < strong> 9765 (8右边的任何内容)。这些数字可以尽可能小,因为它们左边的8确保新数字总是更大。
使数字9765变小的最佳方法是按递增顺序对它们进行排序,给出5679.排序有效,因为最左边的位置值对整体值的贡献最大。因此,我们希望最左边的数字是最小的数字。
这给我们留下了12348 5679 ,这是我们的答案。
注意:我们实际上不必对8号右边的数字进行排序。我们实际上可以改变他们的顺序,因为我们知道从右侧到8的数字是递增的顺序(因为早些时候,我们我们第一次到达一个小于之前数字的数字时停止了。