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的一个特点也是读表。

posted @ 2021-03-04 00:21  Mrsdwang  阅读(49)  评论(0编辑  收藏  举报