快速了解全排列

全排列问题

在这里插入图片描述

递归分析

•用一个数组a[n]来保存1n之间的n个自然数,对于i=1n,每次用a[1]与a[i]交换后,对a[2]~a[n]中的n-1个元素进行全排列

说明:大家轮流来做第1个 ,→ 循环 + 交换。确定好第1个后,对剩下的n-1个再进行全排列→递归

•然后交换a[1]与a[i]的值,使它恢复到此次排列前的状态

已经做过第一个了,请回到原地,轮到下一个当第一个了。

确定好了第一个之后,从剩下的n-1个中轮流选一个来做第2个 → 循环 + 交换 ,确定好了第二个之后,从剩下的n-2个再进行全排列。直到只有一个素的时候,全排列就是自己。

inline void perm(int list[], int k, int n)//求list数组 第k个到第n的全排列。
{
    int i, t;
    if (k == n)
    {
        for (i = 0; i <= n; i++)
        {
            printf("%d", list[i]);
        }
        printf("\n");
    }

    for (i = k; i <= n; i++)
    {
        swap(list[k], list[i]);
        perm(list, k + 1, n); //求k+1~n的全排列
        swap(list[k], list[i]);
    }
}
字典序

全排列生成算法的一个重要思路,就是将集合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]<ak+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]在字典序中的下一个排列。

在这里插入图片描述

int arry[3] = { 1,2,2 };//len==3;  
      //重复序列会去除
    void Permutation()  
    {  
        int len = 3;  
        int j, k;  
      
        while (true)  
        {  
            printf("%d%d%d\n", arry[0], arry[1], arry[2]);  
      
            for (j = len - 2; j >= 0 && arry[j] >= arry[j + 1]; j--);//注意此处 j >= 0 判断条件在前,加个等号即可  
      
            if (j < 0) return;//结束  
      
            for (k = len - 1; k > j&&arry[k] <= arry[j]; k--);//加个等号即可  
      
            swap(arry[k], arry[j]);  
      
            for (int l = j + 1, r = len - 1; l < r; l++, r--)  
                swap(arry[l], arry[r]);  
           
        }  
    }  

从原理到应用

我们已经知道了实现原理,现在来说一下使用吧,

不知道大家是否还记得STL—“algorithm" 中的两个函数next_permutation和prev_permutation

  • next_permutation: 对于当前的排列,如果在字典序中还存在下一个排列,返回真,并且将下一个排列赋予当前排列,如果不存在,就把当前排列进行递增排序。
  • prev_permutation: 对于当前的排列,如果在字典序中还存在前一个排列,返回真,并且将前一个排列赋予当前排列,如果不存在,就把当前排列进行递减排序。
    #include<iostream>  
    #include<algorithm>  
      
    using namespace std;  
      
    int arry[3] = { 1,2,3 };//len==3;  
      
    void Permutation()  
    {  
        do  
            printf("%d%d%d\n", arry[0], arry[1], arry[2]);  
        while (next_permutation(arry, arry + 3));  
          
    }  
      
    int main()  
    {  
      
        Permutation();  
      
        return 0;  
    }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章