這兩個函數都包含在algorithm庫中。STL提供了兩個用來計算排列組合關係的算法,分別是next_permutation和prev_permutation。c++
首先咱們來看看這兩個函數的函數原型:算法
1 template< class BidirIt >bool next_permutation( BidirIt first, BidirIt last ); 2 template< class BidirIt, class Compare >bool next_permutation( BidirIt first, BidirIt last, Compare comp );
1 template< class BidirIt >bool prev_permutation( BidirIt first, BidirIt last); 2 template< class BidirIt, class Compare >bool prev_permutation( BidirIt first, BidirIt last, Compare comp);
first,end ——從新排序的元素範圍數組
comp —— 自定義比較函數函數
顧名思義,next_permutation就是求下一個排列組合,而prev_permutation就是求上一個排列組合。首先咱們必須瞭解什麼是「下一個」排列組合,什麼是「前一個」排列組合。考慮由三個字符所組成的序列{a,b,c}。
spa
那麼按照字典序排升序他們一共有下面這幾種排列方式:code
若是給定排列方式P,令P爲{acb},那麼next_permutation即求P+1也就是{bac},prev_permutation也就是求P-1即爲{abc}。固然也能夠自定義謂詞函數進行自定義的「下一個排列組合」。blog
下面是示範代碼:排序
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 5 int main(){ 6 int a[] = {1,2,3}; 7 do{ 8 for(int i = 0; i < 3; i++) cout << a[i] << ' '; 9 cout << endl; 10 }while(next_permutation(a,a+3)); 11 12 cout << endl; 13 do{ 14 for(int i = 0; i < 3; i++) cout << a[i] << ' '; 15 cout << endl; 16 }while(prev_permutation(a,a+3)); 17 return 0; 18 }
預計上面代碼的運行結果應該是輸出兩組1,2,3的排列組合方式,共12行,但實際運行結果以下:原型
Why?it
咱們試着輸出第一個循環後a數組的排列方式,結果會獲得1 2 3,這是由於當next_permutation去找下一個排列組合P+1,找到則排列爲下一個排列組合返回true,不然數組變成字典序最小的排列組合(即爲重置排列)並返回false,prev_permutation也同理。
咱們先想一想如何實現next_permutation,prev_permutation 的原理與之相似。根據字典序,若是排列爲正序既從小到大排列,是一組排列組合中最小的排列方式,而逆序既從大到小排列,則是一組排列組合中最大的排列方式。
在n個元素的排列全排列中,從給定排列P 求解下一排列P+1 ,假設兩個排列組合有前k位是相同的,那麼咱們只須要在後面n-k個元素的排列 P(n-k)中求得下一個排列便可。既然咱們須要的是後面 n-k位的排列,那麼直接從後向前考察。
例如排列 1 2 3 4 5,這是一個正序排列,所以全排列中最小的排列,記爲P.
如今要求P+1,設P1=P+1,P1是 1 2 3 5 4. 能夠發現P1的前三位和P的前三位徹底相同,惟一不一樣的是後兩位順序顛倒,最後兩位從正序的 4 5 變成了逆序的 5 4.
接着求P1+1.設P2 = P1+1,P2是 1 2 4 3 5. 由於最後兩位已是逆序,不可能有字典序更大的排列,所以必須考慮更多的位,在後3個元素中,3 5 4 顯然不是逆序,因此存在字典序更大的排列方式,咱們由此肯定了n-k=3
如今要在 3 5 4 中求得下一個排列,3 5 4 不是一個逆序,是由於 3 後面有元素大於3 。因此咱們要在大於3的數字中選擇最小的那個和3交換,保證獲得最小的首位元素。選擇將3和4進行交換,而不是3 和 5,這樣獲得的首位元素是4. 如今咱們獲得了排列 4 5 3 。
顯然,4 5 3 並非咱們想要的下一個排列,下一個排列是 4 3 5. 觀察區別,不難看出,首位元素必定是4,可是5 3 這個子排列是一個逆序排列。將逆序排列反向後,獲得的正序排列是所能造成的最小排列,所以,4 3 5 是4 爲首位元素所能造成最小排列,而前3 位沒有變化,故咱們獲得了下一排列P2.
另外,大於3的最小元素,即4 ,也是第一個大於3的元素,由於 5 4 是個逆序排列。
對於可重集排列 1 2 3 5 4 3 2 1也同理,首先找到第一個非逆序元素,這裏是3,而後從後向前尋找第一個大於3的元素,這裏是4,交換,獲得 4 5 3 3 2 1 的子排列,而後反向,便可獲得下一排列。若是沒有找到第一個非逆序元素,那麼說明該排列已是最大排列。
代碼:
1 template<class T> 2 bool next_permutation(T begin, T end){ 3 T i = end; 4 if (begin == end || begin == --i) return false; 5 6 while(1){ 7 T i1 = i,i2; 8 if (*--i < *i1) { //找第k位 9 for(i2 = end; *i >= *i2; i2--);//從後往前找到逆序中大於*i的元素的最小元素 10 iter_swap(i, i2); 11 reverse(i1, end);////將尾部的逆序變成正序 12 return true; 13 } 14 if (i == begin) { 15 reverse(begin, end); 16 return false; 17 } 18 } 19 }
prev_permutation的代碼實現也相似,這裏再也不給出,各位能夠自行嘗試。
最多n/2次交換,n爲區間長度,平均每次調用使用了3次比較和1.5次交換,時間複雜度爲O(n)。