全排列的算法很多,大致有dfs、swap、字典序、康拓展开等。这些算法的时间复杂度最少都是n!
1. dfs
深搜回溯是解决全排列最简单的方法:
dfs(a[], dep):
if dep = a.length
do(r[])
return
for i: 0->a.length
if v[i] = false
v[i] = true
r[dep] = a[i]
dfs(a, dep + 1)
v[i] = false
r[dep] = null
2. swap
这个方法是基于这么个公式,
f(a)=a
f(ab) = af(b), bf(a)
f(abc) = af(bc), bf(ac), cf(ab)
perm-swap(dep):
if dep >= a.length
do()
return
for i: dep->a.length
a[i]<-->a[dep]
perm-swap(dep + 1)
a[i]<-->a[dep]
3. 字典序
将递归转化为迭代,可以采用映射。这个方法将n个数的全排列映射成n进制数,比如:
0 ,1, 2 可以看出3进制,初始序列为012
012+1=020,重复,不和要求
020+1=021,这个是
021+1=102,这个是
102+1=110,重复
....
200+1=210,终止
4. 康拓展开
方法3不是一一映射,算法的复杂度被提升为n^n。这个方法使用一一映射
123=0*2!+0*1!+0*0!=0
132=0*2!+1*1!+0*0!=1
213=1*2!+0*1!+0*0!=2
231=1*2!+1*1!+0*0!=3
312=2*2!+0*1!+0*0!=4
321=2*2!+1*1!+0*0!=5
公式:X=a(n)*(n-1)! + a(n-1)*(n-2)!+ ... + a(n) * 0!
仔细观察会发现,a(n)其实表示在n位之后比a[n]小的数的个数。那么,这个映射的原理是什么?
再仔细观察会发现,值域表示小于这个排列的排列数,如123是0, 321是5
以231为例子:
1,第一位是2,如果把2换成1,那么1XX肯定比231小,所以当2换成1时,有2种排列比231小
2. 第一位2不变,把3换成 1, 231>213,所有这种情况有1种比231小
所以231=1*2! + 1*1! + 0*0! = 3
那么按这个方法计算全排列,自然康拓逆展开,比如把f(3)=231,计算过程:
1. 3/2! = 1
2. (3-1)/1!=2
3. (2-2)/0!=0
所以结果是231
可以看出,康拓展开实际上是一种压缩算法
阅读(1296) | 评论(0) | 转发(0) |