338. Counting Bits
仅供自己学习
思路:
这道题必须要知道一个知识点,如何进行通过位运算获得 位为1的数量。这个公式是 i&(i-1),原因是相邻的两个数相差为1,即使在二进制中,也只是最低位有一个1的差距造成其他位的不相同,而其引起的变化相与,除了i为2的次幂的数外,都会存在与为1的情况,用该公式每次相与后都会使二进制位中最低为1的一个位变为0,也就是为1的位的数量只会少1。
所以我们只需要对 0~num中的每个数进行 x=x&(x-1)的处理,处理的次数就是有多少为1的位,直到处理后x为0就停止。
代码:
1 class Solution { 2 public: 3 int countones(int x){ 4 int ones=0; 5 while(x>0){ 6 x = x&(x-1); 7 ++ones; 8 } 9 return ones; 10 } 11 vector<int> countBits(int num) { 12 vector<int> res; 13 for(int i=0;i<=num;++i){ 14 res.push_back(countones(i));, 15 } 16 return res; 17 } 18 };
动态规划的方法:
通过规律,利用最高有效位,用已有的答案进行O(1)的加法计算。
二进制中,当转化为十进制后为2的次幂的数的二进制,只存在一个位为1,而这时候就是我们更新最新有效位的时候。根据公式 res[i]=res[i-highbit]+1,因为每次达到2的次幂的数后,就相当于在前一个位进了1,而后面的位又全归为0,我们可以将后面这些归0的位视为从0又重新开始计数,所以当我们到2的次幂后,我们就会更新highbit,此时i-highbit就会从0开始到达highbit,因为此时原来归0的位又全部为1了,此时又会向新的位进1,然后又开始后面的数归0后,相当于又从0开始计数到highbit。加1的原因是 这个1代表新进位的那个1,就是最高有效位的那个1,相当于我们每次遇到一个2的次幂的数,我们都会重新从0开始一直+1到这个2的次幂的数,然后会再次遇到下一个2的次幂的数,再重复此步骤,于是我们将重复利用以求出来的结果加一即可。
代码:
1 class Solution { 2 public: 3 4 vector<int> countBits(int num) { 5 vector<int> res(num+1); 6 int highbit=0; 7 for(int i=0;i<=num;++i){ 8 if((i&(i-1))==0){ 9 highbit=i; 10 } 11 res[i]=res[i-highbit]+1; 12 } 13 return res; 14 } 15 };
还有一个通过最低有效位来解决的方法:
存在规律:当x位偶数时,res[x]=res[x/2],当x为奇数时,res[x]=res[x/2]+1,这里可以通过两个技巧统一起来
第一个:x/2可等效为x>>1,将x右移一位
第二个:x%2可等效为x&1,将x与1相与,因为二进制只有偶数时最低为才不会为1,所以与1相与就能判断,为奇数则相与最低位为1,偶数则为0.
代码:
1 class Solution { 2 public: 3 4 vector<int> countBits(int num) { 5 vector<int> res(num+1); 6 7 for(int i=1;i<=num;++i){ 8 res[i]=res[i>>1]+ (i&1); 9 } 10 return res; 11 } 12 };
定义最低设置位为二进制中最低的1所在的位。因为x&(X-1)得到的数的为1的位的数量减少1,所以我们直接定位res[i]=res[i&(i-1)]+1即可,对所有0到num的数都进行即可
代码:
1 class Solution { 2 public: 3 4 vector<int> countBits(int num) { 5 vector<int> res(num+1); 6 for(int i=1;i<=num;++i){ 7 res[i]=res[i&(i-1)]+ 1; 8 } 9 return res; 10 } 11 };
上面三种方法都是DP,通过读表来提高运行速度。而DP的一个特点也是读表。