全排列及相關擴充套件演算法(二)——求字典序下一組排列及全排列演算法

farsun發表於2021-09-09

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
template inline
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同理

template inline
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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章