網站首頁 編程語言 正文
前言
next_permutation/prev_permutation是C++ STL中的一種實用算法;
功能是: 以迭代器的方式,將一個容器內容改變為他的下一個(或prev上一個)全排列組合;
next_permutation的使用
假設需要將字符串abcd的全排列依次打印,我們可以用next_permutation函數方便操作:
使用方法:
1.一般先sort成升序;(prev_permutation倒著全排列使用規則相反)
2.然后再do while配合調用全排列,循環輸出,直到排列情況全部輸出 返回false;
運行結果:
實現全排列的兩種算法
當然僅僅只會用沒有什么困哪的,如果面試官突然問你STL中這個全排列算法咋實現的呢?
1. 遞歸法(全排列方便理解記憶的方法,作為備用方法)
如果上面全排列,突然腦袋斷片了,或者說考試中不讓用封裝好的庫函數;
為了不至于連個全排列的思想都不會,可以用用相對好理解的遞歸法全排列:
算法思想:(遞歸問題:按規則處理一個過程,剩下的過程是相同的處理方式,那么就可以進行函數遞歸調用)
1.從集合中依次選出每一個元素,作為排列的第一個元素;
2.然后**對剩余的元素(第一個元素之后的)進行 同樣操作 **;
如此遞歸處理,從而得到所有元素的全排列。
eg:
以對字符串abc進行全排列為例,我們可以這么做:以abc為例
- 固定a,遞歸求后面bc的排列:求好: abc,acb后,b交換到第一位置,得到bac,如下固定b遞歸b后面的排列:
- 固定b,求后面ac的排列:bac,bca,求好后,c交換到第一位置,得到cba,如下固定c遞歸c后面的排列:
- 固定c,求后面ba的排列:cba,cab;結束 一共a,b,c分別當第一個元素進行了全排列,算法結束;
(注意:1. 每次交換下一個位置的時候,需要swap換回來,保證原始序列,再交換下一個位置的字母去第一個位置。2. 需要考慮有重復相同且挨著的數字情況,此時需要剪枝)
遞歸比較抽象,可以用簡單地例子abc在紙上模擬畫一下好理解;
實現代碼(無重復元素情況)
#include<iostream>
#include<vector>
using namespace std;
//dfs實現全排列(無重復元素情況)
void dfs(string &s, int l, int r)
{
if (l == r) {//遞歸終止,當前s可以輸出了,已經是某一輪的完整排列,不能再排列了
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換回來原來的位置!否則會出現重復排列!
}
}
int main()
{
string s = "abcd";
//sort(s.begin(),s.end())//sort一下,再配合dfs算法,可以實現按照字典序處理
int len = s.size();
dfs(s,0,len);
return 0;
}
運行結果:
有重復元素情況
這種情況需要對代碼做一個優化,不然的話按照上面的算法,會出現重復數字的重復排列情況;
優化很簡單:
如果某個數字在之前的排列被換到首位置進行排列過,那本次交換就不進行;(其次,當dfs首次交換i==l的時候,即便出現過也需要進行);
整合一下就是if(i==l || s[i]沒出現) -->進行交換)
代碼:
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
//dfs實現全排列(含重復元素情況)
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()) {//防止后續進行重復排列
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底層原理)
比較抽象,難以理解,根據個人情況來理解;
一個全排列可看做一個字符串,字符串可有前綴、后綴。
規定: 生成給定全排列的下一個排列–> 所謂一個字符串的下一個排列,就是這個個字符串變化限制在盡可能短的后綴上,變化后的那個字符串; 這就要求這一個與下
一個有盡可能長的共同前綴;變化限制在盡可能短的后綴上
eg:
839647521是1—9的排列。1—9的排列最前面的是123456789,最后面的987654321;
從右向左掃描若都是增的,就到了987654321,也就沒有下一個了。否則找出第一次出現下降的位置。
如何得到346987521的下一個?
首先對原生字符串排序,這個迭代算法是基于字典序排序好的字符串全排列;(所以之后從尾部,向前循環迭代,每次變化盡可能短的后綴,以此類推)
1.從尾部往前找第一個P(i-1) < P(i)的位置;: 346987521 種 最終找到6是第一個變小的數字,記錄下6的位置i-1
2.從找到的 i 位置往后找到最后一個大于6的數:346987521 中最終找到7的位置,記錄位置為m(m == r-1)
3.swap(r-1,i-1) : 3 4 7 9 8 6 5 2 1
4.倒序翻轉i位置后的所有數據 : 3 4 7 1 2 5 6 8 9
5.進行do-while循環,直到第一步之后,判斷出 i==0 break; 全部排列完畢
很抽象,但是思想就是 一個字符串的下一個全排列:有盡可能長的共同前綴;變化限制在盡可能短的后綴上
大概知道步驟: 那么用排好序的123456789試試上面的過程,你會發現,每次變化盡可能短的后綴,有點像遞歸的感覺,一步一步逼近字符串0,index,此時完美結束; 或者用123這個例子體驗一下6個全排列是咋來的 amazing
上面的過程多悟幾遍就理解了; 大概知道思想面試的時候說說也行=-=
實現代碼(有無重復不影響)
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
//dfs實現全排列
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. 翻轉 i后面的子串
} while (1); //do while為了讓第一個 abcd 也正常打印再全排列
return 0;
}
這個算法理解起來對于我來說有點夸張,嗚嗚嗚;
原文鏈接:https://blog.csdn.net/wtl666_6/article/details/128818981
相關推薦
- 2022-10-06 C++?abs函數實際應用詳解_C 語言
- 2022-06-27 Python查找多個字典公共鍵key的方法_python
- 2022-09-09 C#流程控制詳解_C#教程
- 2022-10-16 Python?Celery動態添加定時任務生產實踐指南_python
- 2022-07-12 springboot整合jasypt加密yml配置文件
- 2022-09-16 Go?WEB框架使用攔截器驗證用戶登錄狀態實現_Golang
- 2022-04-24 TypeScript基礎class類教程示例_基礎知識
- 2023-03-16 Python中的functools?partial詳解_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支