全排列生成算法 .

参考链接: 全排列生成算法(一)
原文讲的很详细了,为了完整性,这里粘贴的参考链接中大部分文字,并且在原文的基础上,添加了“给定某个排列,求其字典序中上一个元素”的算法。

字典序

全排列生成算法的一个重要思路,就是将集合A中的元素的排列,与某种顺序建立一一映射的关系,按照这种顺序,将集合的所有排列全部输出这种顺序需要保证,既可以输出全部的排列,又不能重复输出某种排列,或者循环输出一部分排列。字典序就是用此种思想输出全排列的一种方式这里以A{1,2,3,4}来说明用字典序输出全排列的方法。

首先,对于集合A的某种排列所形成的序列,字典序是比较序列大小的一种方式以A{1,2,3,4}为例,其所形成的排列1234<1243,比较的方法是从前到后依次比较两个序列的对应元素,如果当前位置对应元素相同,则继续比较下一个位置,直到第一个元素不同的位置为止,元素值大的元素在字典序中就大于元素值小的元素上面的a1[1...4]=1234和a2[1...4]=1243,对于i=1,i=2,两序列的对应元素相等,但是当i=2时,有a1[2]=3<a2[2]=4,所以1234<1243

求字典序的下一个全排列:
使用字典序输出全排列的思路是,首先输出字典序最小的排列,然后输出字典序次小的排列,……,最后输出字典序最大的排列这里就涉及到一个问题,对于一个已知排列,如何求出其字典序中的下一个排列这里给出算法。
  • 对于排列a[1...n],找到所有满足a[k]<a[k+1](0<k<n-1)的k的最大值,如果这样的k不存在,则说明当前排列已经是a的所有排列中字典序最大者,所有排列输出完毕
  • 在a[k+1...n]中,寻找满足这样条件的元素l,使得在所有a[l]>a[k]的元素中,a[l]取得最小值。也就是说a[l]>a[k],但是小于所有其他大于a[k]的元素
  • 交换a[l]与a[k].
  • 对于a[k+1...n],反转该区间内元素的顺序。也就是说a[k+1]与a[n]交换,a[k+2]与a[n-1]交换,……,这样就得到了a[1...n]在字典序中的下一个排列。
这里我们以排列a[1...8]=13876542为例,来解释一下上述算法。首先我们发现,1(38)76542,括号位置是第一处满足a[k]<a[k+1]的位置,此时k=2所以我们在a[3...8]的区间内寻找比a[2]=3大的最小元素,找到a[7]=4满足条件,交换a[2]和a[7]得到新排列14876532,对于此排列的3~8区间,反转该区间的元素,将a[3]-a[8],a[4]-a[7],a[5]-a[6]分别交换,就得到了13876542字典序的下一个元素14235678

求字典序的上一个全排列:
从前面的的分析,我们可以进行逆推导,求得上一个排列,这里的算法如下:
1. 对于排列a[1..n],从尾端开始,找到第一个a[k]>a[k+1]并记录,若k<0,则说明a已经是字典序的最小者。
2. 在a[k+1..n]中,寻找小于a[k]的最大元素a[i],
3. 交换a[k]和a[i]的值,
4. 对于a[k+1..n],反转区间内元素的顺序。
这里我们还是以前面的例子来说明,初始时,a=14235678,首先找到1(42)35678,此时k=2,再找到比4小的最大数是3,此时a[i]=3, i=4, 交换a[i]和a[k]的值,得到a=13245678,最后反转a[k+1..n],得到最后的结果a=13876542。

C代码如下:
  1. /* 
  2. http://t.jobdu.com/thread-98707-1-1.html 
  3. 1、写出一个算法来生成1-n的全排列 
  4. 2、已知一个长度为N的序列A,它的每个元素是1-N之间的任何一个元素,且两两不重复,称他为一个排列,写出一个算法生成该排列的字典序的下一排列。例如,A=[3 2 1 4],则下一排列为A'=[3 2 4 1],A'的下一排列为A''=[3 4 1 2],以此类推。 
  5. http://blog.****.net/joylnwang/article/details/7064115 
  6. */  
  7.   
  8. #include <stdio.h>   
  9.   
  10. void swap(char* array, unsigned int i, unsigned int j)  
  11. {  
  12.     char t = array[i];  
  13.     array[i] = array[j];  
  14.     array[j] = t;  
  15. }  
  16.   
  17. // 递归输出序列的全排列   
  18. void fullPermutation(char* arr, int len, int index)  
  19. {  
  20.     int i;  
  21.     if (index >= len)  
  22.         printf("%s\n", arr);  
  23.     else  
  24.     {  
  25.         for (i = index; i < len; i++)  
  26.         {  
  27.             swap(arr, index, i);  
  28.             fullPermutation(arr, len, index+1);  
  29.             swap(arr, index, i);  
  30.         }  
  31.     }  
  32. }  
  33.   
  34. void reverse(char arr[], int start, int end)  
  35. {  
  36.     if (start >= end)  
  37.         return;  
  38.     int len = start - end + 1;  
  39.     int i;  
  40.     for (i = 0; i <= len/2; i++)  
  41.     {  
  42.         swap(arr, start+i, end-i);  
  43.     }  
  44. }  
  45.   
  46. int pre_permutation(char arr[], int len)  
  47. {  
  48.     int k, i, max, max_i;  
  49.     for (i = len-2; i >= 0; i--)  
  50.     {  
  51.         if (arr[i] > arr[i+1])  
  52.         {  
  53.             k = i;  
  54.             break;  
  55.         }  
  56.     }  
  57.     if (i < 0)  
  58.     {  
  59.         printf("%s is the first permutation\n", arr);  
  60.         return -1;  
  61.     }  
  62.     max_i = k + 1;  
  63.     max = arr[max_i];  
  64.     for (i = k+1; i < len; i++)  
  65.     {  
  66.         if (arr[i] < arr[k] && arr[i] > max)  
  67.         {  
  68.             max_i = i;  
  69.             max = arr[max_i];  
  70.         }  
  71.     }  
  72.     if (max_i < len)  
  73.     {  
  74.         swap(arr, k, max_i);  
  75.         reverse(arr, k+1, len-1);  
  76.     }  
  77.     return 0;  
  78. }  
  79.   
  80. int next_permutation(char arr[], int len)  
  81. {  
  82.     int k, i, min, min_i;  
  83.     for (k = len-2; k >= 0; k--)  
  84.     {  
  85.         if (arr[k] < arr[k+1])  
  86.             break;  
  87.     }  
  88.     if (k < 0)  
  89.     {  
  90.         printf("%s is the last permutation\n", arr);  
  91.         return -1;  
  92.     }  
  93.     min = arr[k+1];  
  94.     min_i = k+1;  
  95.     for (i = k + 1; i < len; i++)  
  96.     {  
  97.         if (arr[i] > arr[k] && arr[i] < min)  
  98.         {  
  99.             min_i = i;  
  100.             min = arr[i];  
  101.         }  
  102.     }  
  103.     if (min_i < len)  
  104.     {  
  105.         swap(arr, k, min_i);  
  106.         reverse(arr, k+1, len-1);  
  107.     }  
  108.     return 0;  
  109. }  
  110.   
  111. int main()  
  112. {  
  113.     int i = 1;  
  114.     char arr[] = "1234";  
  115.     int len = sizeof(arr) / sizeof(arr[0]) - 1;  
  116.     //fullPermutation(arr, len, 0); // 递归打印全排列   
  117.   
  118.     // 按照字典序输出全排列   
  119.     printf("next_permutation demo:\n");  
  120.     do  
  121.     {  
  122.         printf("%02d: %s\n", i, arr);  
  123.         i++;  
  124.     } while (next_permutation(arr, len) >= 0);  
  125.   
  126.     i = 1;  
  127.     printf("\npre_permutation demo:\n");  
  128.     do  
  129.     {  
  130.         printf("%02d: %s\n", i, arr);  
  131.         i++;  
  132.     }  
  133.     while (pre_permutation(arr, len) >= 0);  
  134.   
  135.     return 0;  
  136. }  
/*
http://t.jobdu.com/thread-98707-1-1.html
1、写出一个算法来生成1-n的全排列
2、已知一个长度为N的序列A,它的每个元素是1-N之间的任何一个元素,且两两不重复,称他为一个排列,写出一个算法生成该排列的字典序的下一排列。例如,A=[3 2 1 4],则下一排列为A'=[3 2 4 1],A'的下一排列为A''=[3 4 1 2],以此类推。
http://blog.****.net/joylnwang/article/details/7064115
*/

#include <stdio.h>

void swap(char* array, unsigned int i, unsigned int j)
{
    char t = array[i];
    array[i] = array[j];
    array[j] = t;
}

// 递归输出序列的全排列
void fullPermutation(char* arr, int len, int index)
{
    int i;
    if (index >= len)
        printf("%s\n", arr);
    else
    {
        for (i = index; i < len; i++)
        {
            swap(arr, index, i);
            fullPermutation(arr, len, index+1);
            swap(arr, index, i);
        }
    }
}

void reverse(char arr[], int start, int end)
{
    if (start >= end)
        return;
    int len = start - end + 1;
    int i;
    for (i = 0; i <= len/2; i++)
    {
        swap(arr, start+i, end-i);
    }
}

int pre_permutation(char arr[], int len)
{
    int k, i, max, max_i;
    for (i = len-2; i >= 0; i--)
    {
        if (arr[i] > arr[i+1])
        {
            k = i;
            break;
        }
    }
    if (i < 0)
    {
        printf("%s is the first permutation\n", arr);
        return -1;
    }
    max_i = k + 1;
    max = arr[max_i];
    for (i = k+1; i < len; i++)
    {
        if (arr[i] < arr[k] && arr[i] > max)
        {
            max_i = i;
            max = arr[max_i];
        }
    }
    if (max_i < len)
    {
        swap(arr, k, max_i);
        reverse(arr, k+1, len-1);
    }
    return 0;
}

int next_permutation(char arr[], int len)
{
    int k, i, min, min_i;
    for (k = len-2; k >= 0; k--)
    {
        if (arr[k] < arr[k+1])
            break;
    }
    if (k < 0)
    {
        printf("%s is the last permutation\n", arr);
        return -1;
    }
    min = arr[k+1];
    min_i = k+1;
    for (i = k + 1; i < len; i++)
    {
        if (arr[i] > arr[k] && arr[i] < min)
        {
            min_i = i;
            min = arr[i];
        }
    }
    if (min_i < len)
    {
        swap(arr, k, min_i);
        reverse(arr, k+1, len-1);
    }
    return 0;
}

int main()
{
    int i = 1;
    char arr[] = "1234";
    int len = sizeof(arr) / sizeof(arr[0]) - 1;
    //fullPermutation(arr, len, 0); // 递归打印全排列

    // 按照字典序输出全排列
    printf("next_permutation demo:\n");
    do
    {
        printf("%02d: %s\n", i, arr);
        i++;
    } while (next_permutation(arr, len) >= 0);

    i = 1;
    printf("\npre_permutation demo:\n");
    do
    {
        printf("%02d: %s\n", i, arr);
        i++;
    }
    while (pre_permutation(arr, len) >= 0);

    return 0;
}


上一篇:分布式系统架构,回顾2020年常见面试知识点梳理(每次面试都会问到其中某一块知识点)


下一篇:excel将宏保存到个人工作簿