全排列問題的STL用法(next_permutation類)
標準庫全排列next_permutation()
在標準庫算法中,next_permutation應用在數列操作上比較廣泛.這個函數可以計算一組數據的全排列.但是怎麼用,原理如何,我做了簡單的剖析.
首先查看stl中相關信息.
函數原型:
template<class BidirectionalIterator>
bool next_permutation(
BidirectionalIterator _First,
BidirectionalIterator _Last
);
template<class BidirectionalIterator, class BinaryPredicate>
bool next_permutation(
BidirectionalIterator _First,
BidirectionalIterator _Last,
BinaryPredicate _Comp
);
兩個重載函數,第二個帶謂詞參數_Comp,其中只帶兩個參數的版本,默認謂詞函數爲"小於".
返回值:bool類型
分析next_permutation函數執行過程:
假設數列 d1,d2,d3,d4……
範圍由[first,last)標記,調用next_permutation使數列逐次增大,這個遞增過程按照字典序。例如,在字母表中,abcd的下一單詞排列爲abdc,但是,有一關鍵點,如何確定這個下一排列爲字典序中的next,而不是next->next->next……
若當前調用排列到達最大字典序,比如dcba,就返回false,同時重新設置該排列爲最小字典序。
返回爲true表示生成下一排列成功。下面着重分析此過程:
根據標記從後往前比較相鄰兩數據,若前者小於(默認爲小於)後者,標誌前者爲X1(位置PX)表示將被替換,再次重後往前搜索第一個不小於X1的數據,標記爲X2。交換X1,X2,然後把[PX+1,last)標記範圍置逆。完成。
要點:爲什麼這樣就可以保證得到的爲最小遞增。
從位置first開始原數列與新數列不同的數據位置是PX,並且新數據爲X2。[PX+1,last)總是遞減的,[first,PX)沒有改變,因爲X2>X1,所以不管X2後面怎樣排列都比原數列大,反轉[PX+1,last)使此子數列(遞增)爲最小。從而保證的新數列爲原數列的字典序排列next。
明白了這個原理後,看下面例子:
int main(){
int a[] = {3,1,2};
do{
cout << a[0] << " " << a[1] << " " << a[2] << endl;
}
while (next_permutation(a,a+3));
return 0;
}
輸出:312/321 因爲原數列不是從最小字典排列開始。
所以要想得到所有全排列
int a[] = {3,1,2}; change to int a[] = {1,2,3};
另外,庫中另一函數prev_permutation與next_permutation相反,由原排列得到字典序中上一次最近排列。
所以
int main(){
int a[] = {3,2,1};
do{
cout << a[0] << " " << a[1] << " " << a[2] << endl;
}
while (prev_permutation(a,a+3));
return 0;
}
才能得到123的所有排列。
---------------------------------------------------------------------------------------------------------------------------------------------
上週見到了一道題,實現可輸入任意字符串,可給出其所有可能排列組合的情況。想了半天,用自己所瞭解的知識都是處理不了(當然長久不用,很生疏了,再加之水平本就不高),在網上搜搜,得出了結果,貼出解決方法來,不太跟得上時代發展的同志們可以借鑑一下。 其實也並沒有多難,現在C++語言中提供了現成的算法來解決排列組合問題,它們分別是next_permutation 和prev_permutation ,需要注意的是 "如果要走遍所有的排列,你必須先將元素排序"。 以下爲轉載: <<C++標準程序庫>>這本書,在看到"變序性算法"部分的時候,發現兩個函數next_permutation,
prev_permutation對於我們平時處理排列組合的問題很有幫助,根據書上的介紹寫了兩個個測試函數:
void func1()
{
vector<int> v;
INSERT_ELEMENTS(v, 1,3);
PRINT_ELEMENTS( v, "myself: ");
while( next_permutation( v.begin(), v.end() ) )
{
PRINT_ELEMENTS( v, "");
}
}
void func2()
{
vector<int> v;
INSERT_ELEMENTS(v, 1,3);
PRINT_ELEMENTS( v, "myself: ");
sort(v.begin(), v.end(), greater<int>() ); //增加排序(降序)
while( prev_permutation( v.begin(), v.end() ) )
{
PRINT_ELEMENTS( v, "");
}
}
如果以後再遇到類似問題,我們就如此如此,不用再費腦筋,人家有現成的函數,直接拿來用就是了。
另外普及一下:
下面這段纔是真真的算法,STL裏面的源碼
template inline
bool next_permutation(_BidIt _First, _BidIt _Last)
{ // permute and test for pure ascending, using operator<
_BidIt _Next = _Last;
if (_First == _Last || _First == --_Next)
return (false);
for (; ; )
{ // find rightmost element smaller than successor
_BidIt _Next1 = _Next;
if (*--_Next < *_Next1)
{ // swap with rightmost element that's smaller, flip suffix
_BidIt _Mid = _Last;
for (; !(*_Next < *--_Mid); )
;
std::iter_swap(_Next, _Mid);
std::reverse(_Next1, _Last);
return (true);
}
if (_Next == _First)
{ // pure descending, flip all
std::reverse(_First, _Last);
return (false);
}
}
}
Ps: "STL":Standard Template Library,標準模板庫(摘錄bbs.csdn.net)
這是最早由Alexander Stepanov和Meng Lee(好像是華人的名字哦)完成,於1994年提交給ANSI/ISO 標準C++委員會並通過而成爲標準C++的一部分。望文生義即可知這是一個代碼庫標準,不是語法標準。簡單地說,STL是以C++中的模板語法爲基礎建立起來的一套包含基礎數據結構和算法的代碼庫。STL的特點是實現了“類型參數化”,即STL的代碼中可處理任意自定義類型的對象,如果不使用模板技術的話,這是一件相當困難的事。也因爲這個原因,在最新的java及C#語法中均加入了對模板語法的支持,可見其重要性。另外一個有關STL重要的話題是GP(Generic
Programming),泛型。這是與面向對象相併列的另外的一個編程模型,它以模板爲基礎,弱化了實體類型的差異,簡化了編程時問題抽象的模型,提供了更好的封裝性和彈性,對於繁雜的面向對象編程毫無疑問是一種解脫,至少是精神上的。GP是最近幾年軟件架構的一個研究熱點,但國內真正的應用似乎並不多見。