全排列及相關擴充套件演算法(二)——求字典序下一組排列及全排列演算法
1.字典序排列的定義:為了便於理解,以數字為例,對於數字1、2、3......n的排列,不同排列的先後關係是從左到右逐個比較對應的數字的先後來決定的。例如對於5個數字的排列 12354和12345,排列12345在前,排列12354在後。按照這樣的規定,5個數字的所有的排列中最前面的是12345,最後面的是 54321。
2.字典序排列解決思路:參考上文中(1,2,3,4)全排列的輸出截圖最後輸出的是(4,1,2,3),並不是我們要求的字典序排列。不難看出字典序最前面的為遞增序列,最後面的位遞減序列,假設我們求字典序中一組排列的下一組排列,我們即可從最後一位往前找,直到找到某一位(i)比其其後一位(i+1)小,那麼說明i之前的位數皆已確定,接下來只需要找到i後面最小的且比i位數大的那一位與i交換作為第i位數(因為在此之前,i後面的位置必然是以遞減的形式存在的),交換後i後面位數按遞增排序,即可構造出原排列的下一組排列。
舉個例子:假設求12453後一組排列
①從最後一位1遍歷,3比5小,往前走
②5比4大,記錄此時位置為i,故i之前的位(12)不變
③遍歷i後面數最小的且比4大的數,即5(j位置)
④4與5交換,形成12543
⑤由於在此之前i的後序排列位遞減序列,故交換後i的後序排列位遞增序列
⑥對i後面的位數進行反轉(4,33,4)形成最終排列12534
3.演算法程式碼
void Reverse(int A[],int a,int b)//反轉 { while (a =0)i--; if (i A[i])&&j注:當i
外部呼叫:
int main() { int A[] = { 1,2,3,4}; int n = sizeof(A) / sizeof(A[0]); //sort(A, A+n ); Print(A,n); while (next_permutation(A, n)) { Count++; Print(A, n); } printf("%dn", Count); Print(A, n); system("pause"); return 0; }4.時間複雜度:單次查詢時間複雜度為O(n),列出所有全排列時間複雜度為O(n*n!)【遍歷所有排列組合n!】+O(nlogn)【一開始的排序】
5.執行截圖
注:當next_permutation返回false時即原排列已經處於遞減序列了(最後一個),這時候返回的序列為遞增序列(第一個)
注:由於在位數篩選時不考慮等於的情況【while ((A[i + 1] =0)i--;】,否則將進入死迴圈(兩位相等的數無限交換)
故此演算法,若原序列中有重複項時並不會輸出相同的排列。
6.STL模版函式:
在STL庫algorithm檔案中給出了此演算法的官方模版函式next_permutation及prev_permutation
以下為原始碼:
next_permutation: // TEMPLATE FUNCTION next_permutation templateinline bool next_permutation(_BidIt _First, _BidIt _Last) {// permute and test for pure ascending, using operator())); } template inline bool next_permutation(_BidIt _First, _BidIt _Last, _Pr _Pred) {// permute and test for pure ascending, using _Pred _DEBUG_RANGE(_First, _Last); return (_Next_permutation_unchecked(_Unchecked(_First), _Unchecked(_Last), _Pred)); } // TEMPLATE FUNCTION next_permutation WITH PRED template inline bool _Next_permutation_unchecked(_BidIt _First, _BidIt _Last, _Pr& _Pred) {// permute and test for pure ascending, using _Pred _BidIt _Next = _Last; if (_First == _Last || _First == --_Next) return (false); //如果迭代器首尾相等或者等於尾地址-1返回false,用於邊界安全。 for (; ; ) {// find rightmost element smaller than successor _BidIt _Next1 = _Next; if (_DEBUG_LT_PRED(_Pred, *--_Next, *_Next1))//找到i點 {// swap with rightmost element that's smaller, flip suffix _BidIt _Mid = _Last; for (; !_DEBUG_LT_PRED(_Pred, *_Next, *--_Mid); );//找到交換的j點 _STD iter_swap(_Next, _Mid); //交換 _Reverse_unchecked(_Next1, _Last); //反序。 return (true); } //我們習慣於先把剪枝處理寫在程式前面,而此演算法這種情況只有一種(最後一個), //不斷地判斷很浪費時間,還不如在最後再反回來。 if (_Next == _First) //回到起點,即處於全排列最後一組的情況,再反回來並return false。 {// pure descending, flip all _Reverse_unchecked(_First, _Last); return (false); } } } // TEMPLATE FUNCTION reverse template inline void _Reverse_unchecked(_BidIt _First, _BidIt _Last) {// reverse elements in [_First, _Last), bidirectional iterators for (; _First != _Last && _First != --_Last; ++_First) _STD iter_swap(_First, _Last); } prev_permutation同理
templateinline bool prev_permutation(_BidIt _First, _BidIt _Last, _Pr _Pred) {// reverse permute and test for pure descending, using _Pred _DEBUG_RANGE(_First, _Last); return (_Prev_permutation_unchecked(_Unchecked(_First), _Unchecked(_Last), _Pred)); } // TEMPLATE FUNCTION prev_permutation template inline bool prev_permutation(_BidIt _First, _BidIt _Last) {// reverse permute and test for pure descending, using operator())); } // TEMPLATE FUNCTION prev_permutation WITH PRED template inline bool _Prev_permutation_unchecked(_BidIt _First, _BidIt _Last, _Pr& _Pred) {// reverse permute and test for pure descending, using _Pred _BidIt _Next = _Last; if (_First == _Last || _First == --_Next) return (false); for (; ; ) {// find rightmost element not smaller than successor _BidIt _Next1 = _Next; if (_DEBUG_LT_PRED(_Pred, *_Next1, *--_Next)) {// swap with rightmost element that's not smaller, flip suffix _BidIt _Mid = _Last; for (; !_DEBUG_LT_PRED(_Pred, *--_Mid, *_Next); ) ; _STD iter_swap(_Next, _Mid); _Reverse_unchecked(_Next1, _Last); return (true); } if (_Next == _First) {// pure ascending, flip all _Reverse_unchecked(_First, _Last); return (false); } } } 7.參考文件
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4289/viewspace-2803933/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 全排列演算法演算法
- 演算法——全排列演算法
- 【Algorithm】全排列演算法Go演算法
- LeetCode46 回溯演算法求全排列,這次是真全排列LeetCode演算法
- 字串全排列字串
- 前端電商 sku 的全排列演算法前端演算法
- 【leetcode】60. Permutation Sequence 全排列的第k位序的排列形式LeetCode
- Leetcode——46. 全排列LeetCode
- leetcode:全排列(java回溯)LeetCodeJava
- LeetCode系列46—全排列LeetCode
- 【LeetCode】46. 全排列LeetCode
- LeetCode-046-全排列LeetCode
- Day 28 | 491.遞增子序列 、46.全排列、 47.全排列 II
- 遞迴-*全排列問題遞迴
- JavaScript陣列元素全排列JavaScript陣列
- LeetCode-047-全排列 IILeetCode
- js陣列全排列問題JS陣列
- 有重複元素的全排列
- 藍橋杯全排列專題
- 位右移 字母大小寫全排列
- 排列組合
- 下一個排列(LeetCode)LeetCode
- 遞迴解決全排列問題遞迴
- 全排列價值(數學問題)
- 程式碼隨想錄演算法訓練營day29 | leetcode 491. 非遞減子序列、46. 全排列、47. 全排列 II演算法LeetCode
- 31. 下一個排列
- 遞迴演算法實踐---實現排列組合遞迴演算法
- 藍橋杯-排列序數
- 從基礎講全排列(Java實現)Java
- LeetCode 45跳躍遊戲&46全排列LeetCode遊戲
- Python語言的全排列怎麼提速?Python
- LeetCode31.下一個排列LeetCode
- 藍橋杯之帶分數(全排列+暴力)
- 回溯法解決全排列問題總結
- 逆序排列
- 字串排列組合問題字串
- LeetCode 31. 下一個排列 | PythonLeetCodePython
- 2014省賽---六角填數(填空)(全排列)