89. 格雷编码
n 位格雷码序列 是一个由 2^n 个整数组成的序列,其中:每个整数都在范围 [0, 2^n - 1] 内(含 0 和 2^n - 1)
- 第一个整数是 0;
- 一个整数在序列中出现 不超过一次;
- 每对相邻整数的二进制表示 恰好一位不同 ,且第一个 和 最后一个 整数的二进制表示 恰好一位不同;
- 给你一个整数 n ,返回任一有效的 n 位格雷码序列 ,其中
1 <= n <= 16。
分析:
若G(n)表示n位格雷编码
-
n = 0
G(0):[ 0 ]
-
n = 1
G(0):[ 0, 0 + 1 ] = [ 0, 1 ] ,倒序为:[ 1, 0 ]
-
n = 2
G(0):[ 0 , 1 , 1 + 2, 0 + 2] = [ 0, 1, 3, 2 ]
-
n = 3
G(0):[ 0, 1, 3, 2 ,2 + 4, 3 + 4, 1 + 4, 0 + 4] = [ 0, 1, 3, 2, 6, 7, 5, 4 ]
…
寻找规律:由于格雷码的规则,相邻位必须只有一个二进制位不相同,起始也满足只有一位二进制位不同,所以G(n)为G(n-1)拼接【Gr(n-1)每一位高位补1,即加2^(n-1)】,如下图,其中Gr表示G的转置数列,如:G(1)=[0,1],则Gr[1]=[1,0]
其中:G(0)=[0],G(1)=[0,1]或[1,0]
方法一:
public List<Integer> grayCode02(int n) {
List<Integer> grayCode = new ArrayList<>();
// 从G(0)开始
grayCode.add(0);
for (int i = 1; i <= n; i++) {
// 偏移量 2^n-1
int offset = 1 << (i - 1);
for (int j = offset - 1; j >= 0; j--) {
// 将G(n-1)倒序取值+offset添加至数组后即可得到G(n)
grayCode.add(grayCode.get(j) + offset);
}
}
return grayCode;
}
方法二:递归的思想,知道递推逻辑,且知道n=0,n=1的结果,递归调用即可得到G(n)
public List<Integer> grayCode(int n) {
if (n == 0) return Arrays.asList(0);
if (n == 1) return Arrays.asList(0,1);
List<Integer> n1 = grayCode(n-1);
List<Integer> n2 = new ArrayList<>(n1.size() * 2);
n2.addAll(n1);
for (int i = n1.size()-1; i>=0; i--) {
n2.add(n1.get(i) + (1<< (n-1)));
}
return n2;
}