• n bits 依次变一位排列的算法


    题目要求对长为n的bit数组输出所有0、1组合,相邻两个只有一个bit发生变化。如果n=2,则可以有下面的输出:

    00 00
    01 01
    11 10
    10 11

    那么左边的是正确的,右边的不正确,因为01和10之间差了两位。

    首先想到的办法是如下递归,设f(n)返回一个长度为n的bit数组,并且数组按照题目要求的那样排列。那么f(n+1)可以如下获得:

    f(n+1)={1+f(n)的每个元素} 连接 {0 +f(n)的所有元素的倒排}

    由于f(n)的元素满足要求,其倒排肯定也满足要求。唯一要证明的是接头的地方是否满足要求。容易看到f(n)的最后一个元素必然等于f(n)倒排的第一个元素,因此连接处也是满足要求的。

    算法如下:

    def changeOneBit(n):
        if(n==0):
            return [[]];
        else:
            xs = changeOneBit(n-1)
            ys0=[]
            for x in xs:
                ys0.append([0]+x)
                ys0.insert(0,[1]+x)
            return ys0

    这个算法有个缺点是需要生成很多临时数组,并且最后需要一个长度为2的n次方的大数组来保存结果。能不能找到一组就立刻输出,不保存呢?回到最初的递归表达式:

    f(n)={0+f(n-1)} 并上 {1+f(n-1)}

    可以得到如下树结构:

    pic

    注意到第三层的11和10是倒过来的,按照原始算法下先是10再是11。由此我们想到有两种递归:

    f(n)={0+f(n-1)} 并上 {1+f(n-1)}先0后1

    f(n)={1+f(n-1)} 并上 {0+f(n-1)}先1后0

    需要结合起来,因此有如下的表达式:

    f(n,bitFlag)={x+f(n-1, bitFlag’)} 并上 {x’+f(n-1, bitFlag’)}

    那么x是0还是1呢,bitFlag’又该怎么变化呢?

    显然bitFlag为0的时候表示正常,为1表示需要翻转。正常情况先0后1,翻转情况先1后0.

    那什么时候需要翻转?注意到如果父节点包含奇数个1则需要翻转。因为算法保证f(n-1)的序列满足相邻数只差一个bit的需求,那么要想它的下一层是相连接的,必须要求相邻的两个子树是镜像。也就是说如果i子树翻转了,i+1就不能翻转。00显然不翻转,故01翻转,11不翻转,10翻转。

    因此算法表达式为:

    f(n,bitFlag)={0+f(n-1, bitFlag ^ x)} 并上 {1+f(n-1, bitFlag ^ x)} bitFlag==0

    f(n,bitFlag)={1+f(n-1, bitFlagx ^ x)} 并上 {0+f(n-1, bitFlag ^ x)} bitFlag==1

    代码如下,if bit0那个可以压缩。但是为了方便理解,就不压缩了:

    line=0;
    def changeOneBit1(a, n, bit0):
        global line;
        if(n==0):
            line=line+1
            print str(line)+':'+str(a);
        else:
            if(bit0==0):
                a[n-1]=0;
                changeOneBit1(a,n-1,bit0 ^ a[n-1]);
                a[n-1]=1;
                changeOneBit1(a,n-1,bit0 ^ a[n-1]);
            else:
                a[n-1]=1;
                changeOneBit1(a,n-1,bit0 ^ a[n-1]);
                a[n-1]=0;
                changeOneBit1(a,n-1,bit0 ^ a[n-1]);

    运行结果如下:

    1:[0, 0, 0, 0]
    2:[1, 0, 0, 0]
    3:[1, 1, 0, 0]
    4:[0, 1, 0, 0]
    5:[0, 1, 1, 0]
    6:[1, 1, 1, 0]
    7:[1, 0, 1, 0]
    8:[0, 0, 1, 0]
    9:[0, 0, 1, 1]
    10:[1, 0, 1, 1]
    11:[1, 1, 1, 1]
    12:[0, 1, 1, 1]
    13:[0, 1, 0, 1]
    14:[1, 1, 0, 1]
    15:[1, 0, 0, 1]
    16:[0, 0, 0, 1]

    总结一下,这个题目其实只是数据排列的一个小小的变化,但是需要透彻的理解递归的本质,写出递归表达式,并进一步描绘递归空间树,找出节点的规律,才能进一步实现算法。
    Technorati Tags: 算法,递归
  • 相关阅读:
    javascript js date ios手机浏览器出现 NAN的问题解决方法
    DWG TrueView 2022
    FloatingActionButton(悬浮按钮)使用学习<一>
    Android多线程编程<一>Android中启动子线程的方法
    Android多线程编程<二>Handler异步消息处理机制之Message
    Java:类的构造函数
    Java:类与对象概念
    APP免邀请码安装
    .NET4安装总进度一直不动的解决办法
    bitmap缩放时抗锯齿
  • 原文地址:https://www.cnblogs.com/alphablox/p/2999178.html
Copyright © 2020-2023  润新知