• 排列算法汇总(下一个排列,全排列,第K个排列)


    一、下一个排列

        首先,STL提供了两个用来计算排列组合关系的算法,分别是next_permutation和prev_permutation。

        next_permutation(nums.begin(),nums.end());//下一个排列

        prev_permutation(nums.begin(),nums.end())//上一个排列

        当返回为1时,表示找到了下一全排列;返回0时,表示无下一全排列

    1.1下一个排列算法过程

    (1)从右到左,找到第一个违反递增趋势的分区数;例如下图的6。

    (2)从右到左,找到第一个比分区数大的改变数;例如下图的7。

    (3)交换分区数和改变数;例如下图的6和7交换。

    (4)颠倒分区数索引的右边所有数字。例如下图的7之后的元素。

    1.2 STL源码剖析中的算法过程

    (1)首先从最尾端开始往前寻找两个相邻元素,令第一元素为*i,第二元素为*ii,且满足*i<*ii。

    (2)找到这样一组相邻元素后,再从最尾端开始往前检验,找出第一个大于*i的元素,令为*j,将i,j元素对调(swap)。

    (3)再将ii之后的所有元素颠倒(reverse)排序。

    1.3 版本一实现细节(C指针实现)

    template<calss BidrectionalIterator>
    bool next_permutation(BidrectionalIterator first,BidrectionalIterator last)
    {
        if(first == lase) return false; /* 空区间 */
        BidrectionalIterator i = first;
        ++i;
        if(i == last) return false;  /*有一个元素 */
        i = last;                    /* i指向尾端 */  
        --i;
        for(;;)
        {
            BidrectionalIterator ii = i;
            --i;
            /* 以上锁定一组(两个)相邻元素 */
            if(*i < *ii)           /* 如果前一个元素小于后一个元素 */
            {
                BidrectionalIterator j = last; /* 令j指向尾端 */
                while(!(*i < *--j));     /* 由尾端往前找,直到遇到比*i大的元素 */
                iter_swap(i,j);          /* 交换i,j */
                reverse(ii,last);        /* 将ii之后的元素全部逆序重排 */
                return true;
            }
            if(i == first)       /* 进行至最前面了 */
            {
                reverse(first,last);    /* 全部逆序重排 */
                return false;
            }
        }
    }

     1.4版本二实现细节(纯STL规范)

     1 template<typename BidiIt>
     2 bool next_permutation(BidiIt first,BidiIt last)
     3 {
     4       const auto rfirst=reverse_iterator<BidiIt>(last);+++
     5       const auto rlast=reverse_iterator<BidiIt>(first);
     6 
     7       auto pivot=next(rfirst);
     8 
     9       while( pivot!= rlast && *pivot >= *prev(pivot))
    10              ++pivot;//直到找出第一个违反递增趋势的分区数,此时,pivot指向分区数;
    11 
    12       if(pivot == rlast)
    13      {
    14              reverse(rfirst,rlast);//如果此序列为递减系列,则下一个排序为颠倒整个序列;
    15              return false;
    16      }
    17 
    18       auto change=find_if(rfirst,rlast,bindlst(less<int>(),*pivot));//从右到左,找到第一个大于分区数的数,并赋给change;
    19 
    20       swep(*change,*pivot);//交换分区数与改变数;
    21 
    22       reverse(rfirst,pivot);//将分区数之后的序列颠倒;
    23 
    24       return true;      
    25 }

     1.5 前一个排列(prev_permutation)

        与next_permutation类似,STL也提供一个版本:

     1 int prev_permutation(int *begin, int *end)
     2 {
     3     int *i=begin, *j, *k;
     4     if (i==end || ++i==end) return 0;   // 0 or 1 element, no prev permutation
     5     for (i=end-1; i!=begin;) {
     6         j = i--;    // find last decreasing pair (i,j)
     7         if (!(*i > *j)) continue;
     8         // find last k which less than i,
     9         for (k=end; !(*i > *(--k)););
    10         iter_swap(i,k);
    11         // now the range [j,end) is in ascending order
    12         reverse(j,end);
    13         return 1;
    14     }
    15     // current is in ascending order
    16     reverse(begin,end);
    17     return 0;
    18 }

    二、全排列

    1.1 利用next_permutation求全排列

        对初始序列依次求下一个排列,直到没有下一个序列为止。

        举个实例,假设有序列{0,1,2,3,4},下图便是套用上述演算法则,一步一步获得“下一个”排列组合。图中只框出那符合“一元素为*i,第二元素为*ii,且满足*i<*ii ”的相邻两元素,至于寻找适当的j、对调、逆转等操作并未显示出。

    代码如下:

        vector<vector<int>> permute(vector<int>& nums) {
            vector<int>temp;
            vector<vector<int>>result;
            sort(nums.begin(),nums.end());
            do
            {
                temp.clear();
                for(int i=0;i<nums.size();i++)
                    temp.push_back(nums[i]);
                result.push_back(temp);
            }while(next_permutation(nums.begin(),nums.end()));//do while循环最适合,因为先要打印出初始序列
            return result;
        }

    1.2 利用深度优先搜索(DFS)求解,以后待更新。

    三、第K个排列

    简单的,可以用暴力枚举法,调用k-1次next_permutation()(注意一定是k-1次)

    代码如下:

    1     string getPermutation(int n, int k) {
    2         string str(n,'0');
    3         for(int i=0;i<n;i++)
    4             str[i]+=i+1;
    5         for(int i=0;i<k-1;i++)
    6             next_permutation(str.begin(),str.end());
    7         return str;
    8     }
  • 相关阅读:
    开发进度二
    开发进度一
    大道至简阅读笔记01
    用户模板和用户评价
    第九周总结
    第八周总结
    NABCD项目分析
    第七周总结
    第六周总结
    构建之法阅读笔记03
  • 原文地址:https://www.cnblogs.com/zhuruibi/p/8934671.html
Copyright © 2020-2023  润新知