<algorithm>
庫裏有一個名不見經傳的函數叫作:std::next_permutationios
簡單寫個用法示例:面試
cpp#include <iostream> #include <array> #include <algorithm> int main() { std::array<int, 4> A = {1,2,3,4}; do { for (auto i : A) std::cout << i << " "; std::cout << std::endl; } while (std::next_permutation(A.begin(), A.end())); }
輸出(截取部分):算法
1 2 3 4 // 2->3->4 : ascending 1 2 4 3 1 3 2 4 1 3 4 2 1 4 2 3 1 4 3 2 // 4->3->2 : descending 2 1 3 4 2 1 4 3 2 3 1 4
恐怕你已經發現了,這個方法能夠幫助咱們列出某個容器的全套排列組合。它其實還有一個兄弟函數,名曰 std::prev_permutation,一個是向前排列,一個是向後排列,算法上大同小異。函數
探祕code
提到這個,緣由一是這對姐妹藏在深閨無人知,科普一下;更重要的緣由,是我對於其排列的算法很感興趣,它是按照什麼順序來進行排列組合的?這個 STL 算法函數若是讓我來實現,會如何實現?leetcode
實際上,仔細觀察上面輸出,大致上咱們能夠看出一點端倪:get
綜上,咱們若要獲得下一步的組合,應該將注意力集中在遞增的子序列上,設置兩個迭代器,start 與 last,分別指向遞增子序列的手尾,用此來重點分析一下從第二行到第三行的轉變。it
1 2 4 3 ^ ^ s l
這種狀況下, s 指向了 2,意味着 1,2 開頭的組合已經排列完畢,根據第 1 條,咱們但願去排列 1,3 開頭的組合了。而此刻 3 在行尾,咱們就但願將其提取到 2 的前頭去。即變成:io
1 3 2 4
果真就是我們想要的了。但是這個插入過程實際上應分爲兩步:ast
解釋一下第二步,當 last 指向 4,這已經意味着 4 之後必定是降序的,即便交換了 2,3 也沒法改變這個順序。而咱們所但願的,顯然是保持 2,4 這樣的升序,做爲 1,3 開頭排列的起始。因此纔有了第二步的逆序。
動手
講明算法,再來理一理實現思路。
cpp#include <algorithm> template<class Iterator> bool my_next_permutation(Iterator beg, Iterator end) { if (beg == end) return false; // 容器爲空 Iterator start = end; if (beg == --start) return false; // 僅有一個元素 for(;;) { Iterator last = start--; // last 指向最後一個元素,並將 start 前移 if (*start < *last) { // 直至定位到一個升序序列 Iterator riter = end; // 依舊從尾部開始 while (!(*start < *--riter)) // 找到下一個排列的開頭,恰是第一個大於當前開頭的元素 ; std::iter_swap(start, riter); // 交換 std::reverse(last, end); // 逆序 return true; } if (start == beg) { // 特殊狀況,即所有排列完畢(總體降序),回到初始(總體升序) std::reverse(beg, end); return false; } } }
面試
實際上,這道題也是一道面試題,請見:Next Permutation on LeetCode
動手作作吧~