全排列生成算法 .

参考链接: 全排列生成算法(一)

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

字典序

全排列生成算法的一个重要思路,就是将集合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代码如下:

[cpp] view plaincopyprint?

  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.csdn.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.csdn.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;
}
时间: 2024-10-28 18:20:19

全排列生成算法 .的相关文章

java中全排列的生成算法汇总_java

  全排列的生成算法就是对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来.任何n个字符集的排列都可以与1-n的n个数字的排列一一对应,   因此在此就以n个数字的排列为例说明排列的生成法.   n个字符的全体排列之间存在一个确定的线性顺序关系.所有的排列中除最后一个排列外,都有一个后继:除第一个排列外,都有一个前驱.每个排列的后继都可以从它的前驱经过最少的变化而得到,全排列的生成算法就是从第一个排列开始逐个生成所有的排列的方法.   全排列的生成法通常有以下几种:   字典

子集和问题:一种组合生成算法

今天在网上看见这么一道题目:给你m个数,从里面找出和为sum的n个数,问一共能找到多少组这样的 数. 根据我的理解,这是一道组合生成的题目.令m个数组成的集合为M,就是要找到所有元素个数为n且和 为sum的子集. 最笨的方法是生成所有的子集,然后进行验证,这样复杂度为阶乘.显然有一种改进的算法,在笨方 法中,我们连元素个数不为n的子集都生成了,而这显然是不必要的.这种改进想到很容易,但实现起来 有点困难,经过数个小时的艰苦奋战,我终于设计了一种原创性的组合生成算法,虽然它应该早被计算机 科学家想

算法系列(十三) 椭圆的生成算法

椭圆和直线.圆一样,是图形学领域中的一种常见图元,椭圆的生成算法(光栅转换算法)也是图 形学软件中最常见的生成算法之一.在平面解析几何中,椭圆的方程可以描述为(x – x0)2 / a2+ (y – y0)2 / b2 = 1,其中(x0, y0)是圆心坐标,a和b是椭圆的长短轴,特别的,当(x0, y0)就是坐标 中心点时,椭圆方程可以简化为x2 / a2 + y2 / b2 = 1.在计算机图形学中,椭圆图形也存在在点阵 输出设备上显示或输出的问题,因此也需要一套光栅扫描转换算法.为了简化,

算法系列(十一) 圆生成算法

在平面解析几何中,圆的方程可以描述为(x – x0)2 + (y – y0)2 = R2,其中(x0, y0)是圆心坐 标,R是圆的半径,特别的,当(x0, y0)就是坐标中心点时,圆方程可以简化为x2 + y2 = R2.在计算 机图形学中,圆和直线一样,也存在在点阵输出设备上显示或输出的问题,因此也需要一套光栅扫描 转换算法.为了简化,我们先考虑圆心在原点的圆的生成,对于中心不是原点的圆,可以通过坐标的 平移变换获得相应位置的圆. 在进行扫描转换之前,需要了解一个圆的特性,就是圆的八分对 成

算法系列(十) 直线生成算法

在欧氏几何空间中,平面方程就是一个三元一次方程,直线就是两个非平行平面的交线,所以直线 方程就是两个三元一次方程组联立.但是在平面解析几何中,直线的方程就简单的多了.平面几何中 直线方程有多种形式,一般式直线方程可用于描述所有直线: Ax+By+C = 0  (A.B不同 时为0) 当知道直线上一点坐标(X0,Y0)和直线的斜率K存在时,可以用点斜式方程: Y-Y0 = K(X – X0) (当K不存在时,直线方程简化成X = X0) 当知道直线上的两个点 (X0,Y0)和(X1,Y1)时,还可

JS实现的数组全排列输出算法

 这篇文章主要介绍了JS实现的数组全排列输出算法,实例分析了全排列的原理与相关的javascript实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下     本文实例讲述了JS实现的数组全排列输出算法.分享给大家供大家参考.具体分析如下: 这段js代码对数组进行全排列输出,改进了一些老的代码 从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列.当m=n时所有的排列情况叫全排列. ? 1 2 3 4 5 6 7 8 9 10 11 12

求一个密码字典的生成算法!!!!!!!!!!!!!!!!

问题描述 求一个密码字典的生成算法!!!!!!!!!!!!!!!! 求一个密码字典的生成算法,要求生成一个学号,外加6位生日码,结果保存在文本文件中 解决方案 http://blog.csdn.net/rancheice/article/details/8741962 解决方案二: 首先你可以自己定义一个hashcode可以实现,这只不过是简单地实现方式,另外的方法就是使用加密算法,网上有很多的加密算法,直接搬过来就可以了,然后对学号和生日分别加密分别保存,解决!

半字节查表法提问-求教CRC半字节查表法原理和表的生成算法

问题描述 求教CRC半字节查表法原理和表的生成算法 如题 正在研究CRC16的半字节查表法,请各位大神前辈指点下这个的原理和表生成的算法

海外黑客团体将微软XBOX360点数生成算法破解

(编译/星辰细雨)此前微软还对索尼PS3遭到破解事件幸灾乐祸,没过多久,海外黑客团体已经将微软XBOX360点数生成的算法破解,并且公布在网上. 此次海外黑客团体成功破解了XBOX360点数的生成算法,制作出计算器.只需利用一张已经使用过的微软点卡,将上面号码输入黑客网站上的计算器,就可以获得新的点卡号码,每个号码都包含了160微软点数以及48小时的XBOXLIVE体验时间.如此一来同样一个号码可以无限次重复换取大量的游戏点数及LIVE时间.虽然不是每一个号码都包含点数,但是绝大部分都被验证是可