归纳法 (c++)
class Solution {
public:
vector<int> grayCode(int n)
{
vector<int> a;
a.reserve(1 << n);
a.push_back(0);
for (int i = 1; i <= n; i++) {
int num = a.size();
for (int j = num - 1; j >= 0; j--) {
a.push_back(a[j] | (1 << (i - 1)));
}
}
return a;
}
};
个人注解:
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> a;
//reserve():表示容器预留空间,但不是真正的创建对象,需要通过insert()或push_back()等操作创建对象
a.reserve(1 << n);//左移:0010->0100,题目中1 << n即为预留2的n次个位置
a.push_back(0);//将0压入,后续会在0后压入其他数
/*n每增加1,数组长度就增加一倍,但是n的数列可以由n-1的数列本身加上处理后的反转n-1的数列这两个数列相加得到,从第一个0进行处理从而得到n=1(2^1个数)的数列,以此类推*/
for (int i = 1; i <= n; i++) {
int num = ret.size();
//size:指当前容器所存储的元素个数,num一直在变,最开始只有0一个元素,则为1
for (int j = num - 1; j >= 0; j--) {
/*j表示当前长度的最后一个,因为想得到n位的格雷码需要n-1位的和其翻转数列的处理数列相加,要先翻转,所以从后向前表示翻转这一处理*/
a.push_back(a[j] | (1 << (i - 1)));
// |为按位或,如A = 0011 1100,B = 0000 1101,A|B = 0011 1101
}
/*将当前ret中每一个数二进制的情况下最前面的一位变成1,从后向前操作,再依次压入,从一个数到两个数,进行此操作,则二进制有一位不同,满足题目要求,两个数变成四个数,先从两个数中的第二个进行处理然后压入a,则第三个数与第二个数二进制恰好有一位不同,以此类推,当j向前算到n-1位格雷码序列的第一个对应的是n-1位反转后的最后一个,同样进行此操作,而这两位是n位格雷码数组的第一个和最后一个,也恰好一位不同,这种处理方式可以满足题目要求*/
}
//以n=2为例子,最开始只有0,j=0,进行操作后压入1,得到0,1满足题目上各种要求
//之后i=2,j=1,a.push_back(a[1] | (1 << (1))),1左移得到10,为两位,a[1]的值1会自动在前面补上一个0也变成两位,括号中为01|10,得到11即为3并压入
/*等于把01最前面的0变成了1,且相邻的两个恰有一位不一样,之后j--,00|10得到10,即为2,压入*/
/*无需担心不满足题目条件,在从n-1到n时:n-1个先反转,本身是满足相邻不同的,之后从n-1的最后一个数开始处理并压入,则这两个数只有二进制代码中最前面一位(新的部分会在二进制码最前面再加一位1,原来的会自动补上一个0让二者位数相等)不同,其余的没有区别,而后半部分整体等于把前半部分翻转然后在最前面新加一位0,其余不变,再把二者相加,所以不会有区别*/
return a;
}
};
/*格雷码每加1都会让二进制码多一位,比如n=1对应的是2^1个数0,1,n=2则变成00,01,11,10即为0,1,3,2,以此类推n=3时则为三位数,而n由1到2的过程就是先把0,1反转为1,0再把0和1前面都再加上一位1变成11,10,之后本身的0,1会自动在前面加上一位0变成00,01,二者相加得到00,01,11,10,每个都有两位二进制,且处理过程得到的结果满足题目要求,相邻的有一位不一样,首尾有一个不一样,之后转化为十进制即可,其余的以此类推*/