初识全排列
定义:从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。
示例:对数组或者字符串进行全排列时,一般要求得出所有的排列结果。排列结果中的每个元素来自于原始数组,数量和内容与原始数组相同,只是元素的位置发生了改变。
比如对abc字符串进行全排列。则最后其全排列的结果为:abc,acb,bac,bca,cab,cba。
注:当对n各不相同的元素进行全排列时,其最终的排列结果有n!种。
全排列相关的算法
1.交换法
代码如下(示例):
注:在以下的代码中附加了一些c++的相关知识点,可以更好的帮助大家对下列代码的理解。
#include <bits/stdc++.h>
using namespace std;
int cnt=0;
void f(int p,string str,int n){
if(p==n){
cout<<str<<endl;
cnt++;
return;
}
//c++中相关sort函数的使用
//bool compare(int i,int j){
// return i<j; //按照升序进行排列
// return i>j; //按照降序进行排列
//}
//sort(str.begin(),str.end(),compare); //str.begin()进行排列的起始位置,str.end()进行排列的结束位置,compare自定义的用于规定相关的排列方式,此处compare可以省略,当其省略后默认为升序排列
sort(str.begin()+p,str.end()); //按照相关的顺序进行输出 注:此处的起始位置+p是为了保证不影响进行交换了的字符
for(int i=p;i<n;i++){
swap(str[i],str[p]); //每个字符都有成为当前起始字符的机会,试探
f(p+1,str,n); //对后面的字符进行全排列
swap(str[i],str[p]); //返回初始状态,才能保证后面的交换是正确的,回溯
}
}
int main(){
//c++中关于字符串的定义有char str[]="hello"; string str="hello";
//c++若想要获取string类型的长度,可以使用str.length() str.size()获取其相关的长度
//当想要获取类型的长度时,可以使用sizeof(str)/sizeof(str[0]) 注:使用此方法进行获取是需要注意'\0'
char str[10]="acb";
int n = 3;
int p = 0;
f(p,str,n);
cout<<cnt<<endl; //输出全排列的类型数
return 0;
}
2.抽取法
总体思路:抽取可用元素追加到新的数组中,相应的排列结果也在新的数组中。
代码如下(示例):
注:被抽取过的元素不能重复使用。
#include <bits/stdc++.h>
using namespace std;
int cnt = 0; //记录全排列含有多少种方式
int vis[10] = {0}; //记录每个元素是否被访问过
void f(char str[],int n,char b[],int p){
if(p==n){
cout<<b<<endl;
cnt++;
return;
}
for(int i=0;i<n;i++){
if(!vis[i]){ //若未被访问过,则将元素追加到新的数组
vis[i] = 1; //将vis[i]置为1,表示其被访问过
b[p] = str[i];
f(str,n,b,p+1);
vis[i] = 0; //返回到初始状态,保证后面的抽取是正确的
}
}
}
int main(){
char str[10] = "abc";
char b[10]="";
int n=3;
f(str,n,b,0);
cout<<cnt<<endl; //输出全排列的类型数
return 0;
}
3、使用next_permutation方法获取全排列
C++ STL中提供了std::next_permutation与std::prev_permutation可以获取数字或者是字符的全排列,其中std::next_permutation提供升序,std::prev_permutation提供降序。
说明:next_permutation,重复排列范围内的元素(第一,最后一个)返回按照字典序排列的下一个值较大的排列。
返回值:如果有一个更高的排列,它重复排列元素,并返回true;如果这是不可能的(因为它已经在最大可能的排列),它按升序排列所有元素,并返回false。
注:在用此方法进行全排列前,需要是相应的字符串变得有序。
#include <bits/stdc++.h>
using namespace std;
int main(){
string str="bac";
sort(str.begin(),str.end());
int number=0;
do{
cout<<str<<endl;
number++;
}while(next_permutation(str.begin(),str.end()));
cout<<number<<endl;
return 0;
}
总结
在上述的全排列算法中,只适合于对不重复的元素进行全排列。
无论是交换法还是抽取法,在对元素的位置进行交换或追加中,其最后均需要回到初始状态,这样才能保证后面的交换或者追加正确。