C++中字符串全排列算法及next_permutation原理詳解
前言
next_permutation/prev_permutation是C++ STL中的一種實用算法;
功能是: 以迭代器的方式,將一個容器內(nèi)容改變?yōu)樗南乱粋€(或prev上一個)全排列組合;

next_permutation的使用
假設需要將字符串a(chǎn)bcd的全排列依次打印,我們可以用next_permutation函數(shù)方便操作:
使用方法:
1.一般先sort成升序;(prev_permutation倒著全排列使用規(guī)則相反)
2.然后再do while配合調(diào)用全排列,循環(huán)輸出,直到排列情況全部輸出 返回false;

運行結(jié)果:

實現(xiàn)全排列的兩種算法
當然僅僅只會用沒有什么困哪的,如果面試官突然問你STL中這個全排列算法咋實現(xiàn)的呢?
1. 遞歸法(全排列方便理解記憶的方法,作為備用方法)
如果上面全排列,突然腦袋斷片了,或者說考試中不讓用封裝好的庫函數(shù);
為了不至于連個全排列的思想都不會,可以用用相對好理解的遞歸法全排列:
算法思想:(遞歸問題:按規(guī)則處理一個過程,剩下的過程是相同的處理方式,那么就可以進行函數(shù)遞歸調(diào)用)
1.從集合中依次選出每一個元素,作為排列的第一個元素;
2.然后**對剩余的元素(第一個元素之后的)進行 同樣操作 **;
如此遞歸處理,從而得到所有元素的全排列。
eg:
以對字符串a(chǎn)bc進行全排列為例,我們可以這么做:以abc為例
- 固定a,遞歸求后面bc的排列:求好: abc,acb后,b交換到第一位置,得到bac,如下固定b遞歸b后面的排列:
- 固定b,求后面ac的排列:bac,bca,求好后,c交換到第一位置,得到cba,如下固定c遞歸c后面的排列:
- 固定c,求后面ba的排列:cba,cab;結(jié)束 一共a,b,c分別當?shù)谝粋€元素進行了全排列,算法結(jié)束;
(注意:1. 每次交換下一個位置的時候,需要swap換回來,保證原始序列,再交換下一個位置的字母去第一個位置。2. 需要考慮有重復相同且挨著的數(shù)字情況,此時需要剪枝)
遞歸比較抽象,可以用簡單地例子abc在紙上模擬畫一下好理解;
實現(xiàn)代碼(無重復元素情況)
#include<iostream>
#include<vector>
using namespace std;
//dfs實現(xiàn)全排列(無重復元素情況)
void dfs(string &s, int l, int r)
{
if (l == r) {//遞歸終止,當前s可以輸出了,已經(jīng)是某一輪的完整排列,不能再排列了
cout<<s<<endl;
return;
}
for (int i = l; i < r; i++) {
swap(s[l],s[i]);
dfs(s,l+1,r);//遞歸
swap(s[l], s[i]);//進行下一輪的swap dfs,需要先swap換回來原來的位置!否則會出現(xiàn)重復排列!
}
}
int main()
{
string s = "abcd";
//sort(s.begin(),s.end())//sort一下,再配合dfs算法,可以實現(xiàn)按照字典序處理
int len = s.size();
dfs(s,0,len);
return 0;
}
運行結(jié)果:

有重復元素情況

這種情況需要對代碼做一個優(yōu)化,不然的話按照上面的算法,會出現(xiàn)重復數(shù)字的重復排列情況;
優(yōu)化很簡單:
如果某個數(shù)字在之前的排列被換到首位置進行排列過,那本次交換就不進行;(其次,當dfs首次交換i==l的時候,即便出現(xiàn)過也需要進行);
整合一下就是if(i==l || s[i]沒出現(xiàn)) -->進行交換)
代碼:
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
//dfs實現(xiàn)全排列(含重復元素情況)
void dfs(string &s, int l, int r)
{
if (l == r) {
cout<<s<<endl;
return;
}
set<char>st;//檢測重復的set
for (int i = l; i < r; i++) {
if (i == l || st.find(s[i])==st.end()) {//防止后續(xù)進行重復排列
st.insert(s[i]);//滿足 記錄這個字符
swap(s[l], s[i]);
dfs(s, l + 1, r);
swap(s[l], s[i]);
}
}
}
int main()
{
string s = "aba";
int len = s.size();
dfs(s,0,len);
return 0;
}
2. 迭代法(next_permutation底層原理)
比較抽象,難以理解,根據(jù)個人情況來理解;
一個全排列可看做一個字符串,字符串可有前綴、后綴。
規(guī)定: 生成給定全排列的下一個排列–> 所謂一個字符串的下一個排列,就是這個個字符串變化限制在盡可能短的后綴上,變化后的那個字符串; 這就要求這一個與下
一個有盡可能長的共同前綴;變化限制在盡可能短的后綴上
eg:
839647521是1—9的排列。1—9的排列最前面的是123456789,最后面的987654321;
從右向左掃描若都是增的,就到了987654321,也就沒有下一個了。否則找出第一次出現(xiàn)下降的位置。
如何得到346987521的下一個?
首先對原生字符串排序,這個迭代算法是基于字典序排序好的字符串全排列;(所以之后從尾部,向前循環(huán)迭代,每次變化盡可能短的后綴,以此類推)
1.從尾部往前找第一個P(i-1) < P(i)的位置;: 346987521 種 最終找到6是第一個變小的數(shù)字,記錄下6的位置i-1
2.從找到的 i 位置往后找到最后一個大于6的數(shù):346987521 中最終找到7的位置,記錄位置為m(m == r-1)
3.swap(r-1,i-1) : 3 4 7 9 8 6 5 2 1
4.倒序翻轉(zhuǎn)i位置后的所有數(shù)據(jù) : 3 4 7 1 2 5 6 8 9
5.進行do-while循環(huán),直到第一步之后,判斷出 i==0 break; 全部排列完畢
很抽象,但是思想就是 一個字符串的下一個全排列:有盡可能長的共同前綴;變化限制在盡可能短的后綴上
大概知道步驟: 那么用排好序的123456789試試上面的過程,你會發(fā)現(xiàn),每次變化盡可能短的后綴,有點像遞歸的感覺,一步一步逼近字符串0,index,此時完美結(jié)束; 或者用123這個例子體驗一下6個全排列是咋來的 amazing
上面的過程多悟幾遍就理解了; 大概知道思想面試的時候說說也行=-=
實現(xiàn)代碼(有無重復不影響)
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
//dfs實現(xiàn)全排列
int main()
{
string s = "abcd";
sort(s.begin(),s.end());//記得先排序
int len = s.size();
do
{
cout << s << endl;//打印某次排列
int i = s.size() - 1;
int j;
while (i > 0 && s[i] <= s[i - 1]) i--;//1.從后向前 找 第一個 s[i-1]<s[i]
if (i == 0) break;
j = i;
while (j<len && s[j]>s[i - 1]) j++;//2.從i向后 找 最后一個 s[m]>s[i-1] 用j找,所以最后m==j-1
swap(s[i - 1], s[j - 1]);//3. swap (i-1,m(j-1))
reverse(s.begin() + i, s.end()); //4. 翻轉(zhuǎn) i后面的子串
} while (1); //do while為了讓第一個 abcd 也正常打印再全排列
return 0;
}
這個算法理解起來對于我來說有點夸張,嗚嗚嗚;
以上就是C++中字符串全排列算法及next_permutation原理詳解的詳細內(nèi)容,更多關于C++字符串全排列 next_permutation的資料請關注腳本之家其它相關文章!
相關文章
CreateCompatibleDC()函數(shù)案例詳解
這篇文章主要介紹了CreateCompatibleDC()函數(shù)案例詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08
C++數(shù)據(jù)結(jié)構之二叉搜索樹的實現(xiàn)詳解
二叉搜索樹作為一個經(jīng)典的數(shù)據(jù)結(jié)構,具有鏈表的快速插入與刪除的特點,同時查詢效率也很優(yōu)秀,所以應用十分廣泛。本文將詳細講講二叉搜索樹的C++實現(xiàn),需要的可以參考一下2022-08-08
C++實現(xiàn)LeetCode(101.判斷對稱樹)
這篇文章主要介紹了C++實現(xiàn)LeetCode(101.判斷對稱樹),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07

