Chinaunix首页 | 论坛 | 博客
  • 博客访问: 83134
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 340
  • 用 户 组: 普通用户
  • 注册时间: 2013-04-02 20:25
文章分类

全部博文(31)

文章存档

2015年(2)

2014年(29)

我的朋友

分类: Java

2015-11-15 11:38:46

     全排列的算法很多,大致有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) |
0

上一篇:zk--先导篇

下一篇:没有了

给主人留下些什么吧!~~