1、描述
格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数n,打印其格雷编码序列,格雷编码序列必须以0开头。
例1:
输入:2
输出:[0, 1, 3, 2]
解释:对于给定的n,其格雷编码序列并不唯一,[0, 2, 3, 1]也是一个有效的格雷编码序列
00 - 0
01 - 1
11 - 3
10 - 2
例2:
输入:0
输出:[0]
解释:我们定义格雷编码序列必须以0开头,给定的编码总位数为n的格雷编码序列,其长度为 2^n。当 n = 0 时,长度为2^0 = 1。因此,当n=0时,其格雷编码序列为[0]。
2、算法
解法一:镜射排列/动态规划
思想:大问题转化成小问题求解
如果知道了 n = 2 的解的话,如果是 { 0, 1, 3, 2},那么 n = 3 的解就是 { 0, 1, 3, 2, 2 + 4, 3 + 4, 1 + 4, 0 + 4 },即 { 0 1 3 2 6 7 5 4 }。之前的解直接照搬过来,然后倒序把每个数加上 1 << ( n - 1) 添加到结果中即可。
时间复杂度::O(2^n)
func grayCode(_ n: Int) -> [Int] {
/*
动态规划/镜射排列
*/
var gray : [Int] = [Int]()
//初始化n=0的解
gray.append(0)
for i in 0..<n{
//要加的数
let add : Int = 1 << i
//倒序遍历,并且加上一个值添加到结果中
var j = gray.count-1
while j >= 0{
gray.append(gray[j]+add)
j -= 1
}
}
return gray
}
解法二:直接排列/直接推导
思想:
生成格雷码的思路:以二进制为 0 值的格雷码为第零项,第一项改变最右边的位元,第二项改变右起第一个为1的位元的左边位元,第三、四项方法同第一、二项,如此反复,即可排列出n个位元的格雷码。
以 n = 3 为例。
0 0 0 第零项初始化为 0。
0 0 1 第一项改变上一项最右边的位元
0 1 1 第二项改变上一项右起第一个为 1 的位元的左边位
0 1 0 第三项同第一项,改变上一项最右边的位元
1 1 0 第四项同第二项,改变最上一项右起第一个为 1 的位元的左边位
1 1 1 第五项同第一项,改变上一项最右边的位元
1 0 1 第六项同第二项,改变最上一项右起第一个为 1 的位元的左边位
1 0 0 第七项同第一项,改变上一项最右边的位元
时间复杂度: 由于每添加两个数需要找第一个为 1 的位元,需要 O(n),O(n2^n)
func grayCode(_ n: Int) -> [Int] {
/*
直接推导(直接排列)
*/
var gray : [Int] = [Int]()
//初始化n=0的解
gray.append(0)
var i = 1
while i < 1 << n{
//得到上一个的值
var previous = gray[i-1]
//同一项的情况
if i%2 == 1 {
//和0000001 做异或,使得最右边一位取反
previous = previous ^ 1
gray.append(previous)
//同第二项的情况
}else{
var temp = previous
//寻找右边起第一个为1的位元
for j in 0..<n {
if temp&1 == 1 {
//和 00001000000 类似这样的数做异或,使得相应位取反
previous = previous^(1<<(j+1))
gray.append(previous)
break
}
temp = temp>>1
}
}
i += 1
}
return gray
}
解法三:公式/二进制转格雷编码
思想:二进制转格雷码 G(n)=B(n+1) XOR B(n)
利用公式转换即可。即最高位保留,其它位是当前位和它的高一位进行异或操作。
时间复杂度:O(2^n)
func grayCode(_ n: Int) -> [Int] {
/*
公式/二进制转格雷码
*/
var gray : [Int] = [Int]()
var binary = 0
while binary < 1 << n {
gray.append(binary ^ (binary>>1))
binary += 1
}
return gray
}