全排列算法

一、无重复数全排列

方法①:

1. 原理

假设待排序列为 a = { a 1 , a 2 , a 3 , . . . , a n } a = \{a_1,a_2,a_3,...,a_n\} a={a1,a2,a3,...,an},对应的全排列记为 P e r m u ( 1 , n ) Permu(1, n) Permu(1,n),表示索引从 1 1 1 n n n的数组成序列的全排列。它是一个集合,集合中的每个元素是一种排列。比如对于序列 { 1 , 2 , 3 } \{1,2,3\} {1,2,3}
P e r m u ( 1 , 3 ) = { { 1 , 2 , 3 } , { 1 , 3 , 2 } , { 2 , 1 , 3 } , { 2 , 3 , 1 } , { 3 , 1 , 2 } , { 3 , 2 , 1 } } Permu(1,3)=\{ \{1,2,3 \}, \{1,3,2 \}, \{2,1,3 \}, \{2,3,1 \}, \{3,1,2 \}, \{3,2,1 \} \} Permu(1,3)={{1,2,3},{1,3,2},{2,1,3},{2,3,1},{3,1,2},{3,2,1}}

一般的,对于长度为 n n n的序列,
P e r m u ( 1 , n ) = { { a 1 , P e r m u a 1 ( 2 , n ) } , { a 2 , P e r m u a 2 ( 2 , n ) } , . . . , { a n , P e r m u a n ( 2 , n ) } } Permu(1,n)=\{ \{a_1, Permu_{a_1}(2,n)\}, \{a_2, Permu_{a_2}(2,n)\}, ...,\{a_{n}, Permu_{a_n}(2,n)\} \} Permu(1,n)={{a1,Permua1(2,n)},{a2,Permua2(2,n)},...,{an,Permuan(2,n)}}

因此,可以用递归求解。当递归到待排序列只有一个数的时候,排列即为该数,可以返回。所以递归终止条件为:
P e r m u a i ( n , n ) = a i Permu_{a_i}(n,n)=a_i Permuai(n,n)=ai

2. 实现思路如下:

  1. 观察发现, P e r m u ( 1 , n ) Permu(1,n) Permu(1,n)中的每一种排列,实际上是分别将每一个数都作为序列的第一个数,然后剩下的 ( 2 , n ) (2,n) (2,n)的序列再交给递归函数去排列。所以我们可以进行 n n n次循环,每次将索引为 i i i的数置换到第1个位置
  2. 每交换一次,就递归进行 P e r m u ( 2 , n ) Permu(2,n) Permu(2,n)的排列。而 P e r m u ( 2 , n ) Permu(2,n) Permu(2,n)的操作与第1步类似
  3. 排序完后,再交换回来

3. 代码:

int a[] = {1, 2, 3};
void permu(int begin, int end)
{
	if(begin == end) // 待排序列只有一个数
	{
		for(int i = 0; i < 3; i ++ )
			cout << a[i] << " ";
		cout << endl;
		return ;
	}
	for(int i = begin; i <= end; i ++ ) // 进行序列长度次循环
	{
		swap(begin, i); // 每次循环,将索引为i的数作为第1个数
		permu(begin + 1, end);
		swap(i, begin);
	} 
}
int main()
{
	permu(1, n);
	return 0;
}

这个算法中,“排列”的这个动作,实际上是交换操作完成的。若干次交换操作组合在一起,就完成了一次排列。


方法②

这个方法也是我之前直接经常用的。

1.原理

一个排列可以看做:在这 n n n个位置的每一个位置,放一个 1 1 1 n n n没有放过的数。所以可以对位置进行 d f s dfs dfs。每次在当前位置,遍历所有的数,找到没放过的数放入当前位置,然后 d f s dfs dfs下一个位置。

2. 代码

原理比较简单,就直接贴代码了:

int a[] = {1, 2, 3};
int pos[3]; // 排列数组。放的数放到该数组中 
bool st[3]; // 用来判断第i个位置的数有没有被放过
void dfs(int step)
{
	if(step == 3) // 递归了最后一个位置的后一个,即所有的数都放完了,一个排列已经形成,可以返回。
	{
		for(int i = 0; i < 3; i ++ )
			cout << pos[i] << " ";
		cout << endl;
		return ;
	}
	for(int i = 0; i < 3; i ++ )
	{
		if(!st[i]) // 没有被放过
		{
			st[i] = true;
			pos[step] = i; // 将i放入当前位置step
			dfs(step + 1);
			st[i] = false; // 恢复现场
		}
	}
}
int main()
{
	dfs(1); // 从第1个位置开始dfs
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值