一、无重复数全排列
方法①:
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. 实现思路如下:
- 观察发现, 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个位置
- 每交换一次,就递归进行 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. 代码:
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;
}