题目要求对长为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)}
可以得到如下树结构:
注意到第三层的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; |
运行结果如下:
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]