全排列—— 位 法
(算法)全排列—— 位 法
一个能 快速生成全排列的算法叫做 位 法,它之所以 快,是因 位 法中下一个排列 是上一个排列某相
两位 得到的, 只需一步, 就可以得到一个新的全排列,而且 不重复,但是由于每将 n 从一端移 到另一端后,就需要遍 排列 2 次,来 找最大的可移数 m ,所以速度得到了限制。它的原理是:
的全排列可由 [n-1] 的全排列生成:
定 [n-1] 的一个排列 п , 将n 由最右端依次插入排列得到 n 个[n] 的排列:
п ,即
P1 P2
?
P(n -1) n
P1 P2
?
n P(n -1)
n P1 P2
?
P(n-1)
上述 程,一般地,
i,将前一步所得的每一排列重复
i
次,然后将i 由第一排的最后往前移,至最前列,正好走了
i 次 ,下一个接着将i 放在下一排列的最前面,然后依次往后
移,一直下去即得i 元排列。
考 {1,2 ? n} 的一个排列,其上每一个整数都 了一个方向,
如果它的箭 所指的方向的 点小于它本身,我 称整数k
是可移的。
然 1 永 不可移, n 除了以下两种情形外,它都是可移的:
n 是第一个数,且其方向指向左
n 是最后一个数,且其方向指向右 于是,我 可按如下算法 生所有排列:
1,开始 :存在排列123? n,除1 外,所有元素均可移,
即方向都指向左 。
2,当最大元素 n 可移 ,将其从一端依次移 到另一端,即可得到 n-1 个全排列;当 n 移 到某一端后, 不能再移 ,此 找最大的可移数 m ,将 m 与其箭 所指的 数 ( 个数当然不会是 N,否 怎么可移 )
互 位置, 就又得到一个新的全排列;将所得新排列中
所有比 m 大的数 p 的方向 整, 即改 相反方向, 使得
又成了可移数。
3,重复第2 步直到所有的元素都不能移 止。
以 4 个元素的排列 例,首先生成全排列
1234;
找到最大的可移数4,将 4 与其箭 所指的 数互 位置,
可以生成 3 个新排列:
1243
1423
4123
因为没有比4 更大的数 p,所以无需调整p 的方向。最大数
4 到了最左边后, 由于其方向指向左侧, 所以 4 不能再移动。
接下来寻找最大的可移数
3,将
3 与其箭头所指的邻数
2 互
换位置,可以得到新排列:
4132
;
然后将所得排列中比
3 大的数
4 的方向调整, 使得
4 可以移
动,重新成为最大的可移数,将
4 与其箭头所指的邻数互换
位置,可以生成
3 个新排列:
1432
1342
1324
此时最大数
4
到了最右边,又因为其方向指向右侧,所以
4
不能再移动;于是我们寻找最大的可移数
3,将 3 与其箭头
所指的邻数
1
互换位置,可以得到新排列:
3124;
然后将所得排列中比3 大的数 4 的方向调整, 使得 4 可以移
动,重新成为最大的可移数,将4 与其箭头所指的邻数互换
位置,可以生成3 个新排列:
3142
3412
4312
如此循环,直到所有的数都不能移动,即可求出全部排列。
最后得到的一个全排列为:2 1 3 4 ,此时 2 指向左侧, 1,3,
均指向右侧。
根据上述算法分析,使用一个辅助数组来存储各个元素的指
向,我们可以得到代码如下:
/*
函数名称: Permutation
函数功能:排列邻位对换法:输出n 个数的所有全排列
输入变量: int n : 1, 2, 3, ... ,n 共 n 个自然数
输出变量:无
*/
void Permutation(int n)
{
int *a = new int[n]; //用来存储n 个自然数
bool *p = new bool[n]; //用来存储n 个元素的指向:向左
为 false ,向右为 true
for (int i=0; i
排列的数量
{
a[i] = i + 1;
p[i] = false; // 开始均指向左侧
}
do
{
Print(a, n); // 输出第一个全排列
if (n == a[n-1])// 若 n 在最右侧,将其逐次与左侧的元素交换,得到 n - 1 个新的排列
{
for (int i=n-1; i>0; i--)
{
int temp = a[i]; a[i] = a[i-1]; a[i-1] = temp;
bool flag = p[i]; p[i] = p[i-1]; p[i-1] = flag;
Print(a, n);
}
}
else