字典顺序排列,不具有独特的字符

时间:2016-04-30 20:11:14

标签: c algorithm sorting permutation

我想让函数以字典顺序从输入字符串输出所有可能的排列。

我有以下代码:

{{1}}

输入字符串排列是permutations_print已排序。

对于只有独特字符的排列,它没有任何问题,但是我还需要它来处理字符非唯一字符串,任何想法如何调整/修改/工作?我必须使用递归,不应该使用任何类型的排序,我不应该将它存储在任何数组中,只需打印。我想打印所有,甚至重复。

1 个答案:

答案 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的数字是递增的顺序(因为早些时候,我们我们第一次到达一个小于之前数字的数字时停止了。