排列算法匯總(下一個排列,全排列,第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     }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM