全排列—不含重复元素


初识全排列

定义:从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;
}

总结

在上述的全排列算法中,只适合于对不重复的元素进行全排列。
无论是交换法还是抽取法,在对元素的位置进行交换或追加中,其最后均需要回到初始状态,这样才能保证后面的交换或者追加正确。

在 Java 中,我们可以使用回溯算法(Backtracking)解决给定不含重复元素的数组全排列问题。这是一种递归的搜索策略,用于在所有可能的状态组合中查找满足条件的答案。下面是一个基本的步骤: 1. 定义一个回溯函数,它接收当前已排序部分、剩余未排序的数组和结果列表作为参数。 2. 如果数组为空,说明我们找到了一个全排列,将其添加到结果列表中。 3. 对于剩余数组中的每一个元素,尝试将其放置在当前已排序部分的末尾,并将剩余的元素继续传递给下一次递归。 4. 如果数组包含重复元素,那么在回溯过程中检查当前位置是否已经出现过该元素,如果出现则跳过这一选择。 5. 递归地处理下一个位置,直到遍历完所有可能性。 以下是伪代码形式: ```java void backtrack(int[] nums, int start, List<List<Integer>> result) { if (start == nums.length) { result.add(Arrays.asList(nums)); return; } for (int i = start; i < nums.length; i++) { // 探索所有可能的位置 if (i > start && nums[i] == nums[i - 1]) continue; // 跳过重复元素 swap(nums, start, i); // 尝试将nums[i]放在开始位置 backtrack(nums, start + 1, result); // 递归调用,移动到下一个位置 swap(nums, start, i); // 回溯:还原数组到原始状态 } } // 帮助函数,交换数组中两个元素的位置 void swap(int[] nums, int a, int b) { int temp = nums[a]; nums[a] = nums[b]; nums[b] = temp; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值