二进制中1的个数问题 (超详细)
整数 二进制中1的个数
前两天遇到这个问题,第一反应就是%2和>>1这两个操作。
大概思路:
正数:%2,拿到原码(补码)的最右位,对于一个int类型的数进行32次判断,这是比较简单的理解。负数:虽然它的补码需要原码取反加一,但是原码和补码的共同点最右位是一致的,所以%2其实也是可以拿到补码的最右位。
所以这个代码对于整数而言,都是行得通的。
int num = -1;int count1 = 0;for (int i = 0; i < 32;i ) {if (num % 2 != 0) {count1 ;}num = num >> 1;}
当然,这个思路差不多的方法还有
(((n >> i) & 1) == 1)
按位与1,通过&1拿到最右位,>>31次,也可以计算出1的个数。
unsigned int n1 = -1;
将负数通过自动类型的提升赋给一个无符号的整数n1,我们对于正数n1 &2和 >> 操作也一样可以拿到1的个数 ( 原理:整数在计算机内存是以补码形式存在 )
这些方法整个思路呢都是相似的,针对int类型的整数,对其补码进行32次循环操作。
当然,我看到了一个最经典的方法,n & (n-1)。
int n = -1;int count = 0;while (n) {n = n & (n - 1);count ;}printf("%d\n",count2);
刚开始看到这个n&n-1,真的是一脸懵逼,这都是哪跟哪,后面仔细的分析了下,的确也是这么一回事。给你们慢慢分析,逐字解析。
还是从正数负数分开分析 (32位太长,我们用8位代替)
正数:以红色框1为基准,(n-1)操作,补码从右往左开始,遇到1就停住,减去,也就是红色框1右边的0全部变成1,左边的原封不动,所以n&n-1也就是拿到了1左边的数,count ,n重新进入循环判断,如果不为0,就代表补码还存在1,重复操作。
(用原码去理解,正数原码补码都一样)
负数:我们要知道负数的补码是原码取反加一,对于一个(负数-1) & 负数,就很难去理解,所以我也是通过图片帮助理解。
首先我们要知道 n-1在计算机底层的操作是n (-1),我们通过一个数与32位1,也就是-1的补码形式,进行相加,溢出之后,就是n-1的效果,这里必须要再次感叹计算机的创造者。
n-1:依然以红色框1为基准,与-1的补码进行相加,很清楚的可以看到,右边开始,第一个1,也就是红色框1变为0,红色框1左边的数都原封不动,为什么会这么神奇呢? 之后每一位0或者1都是两次 1,依然保持不变,与(-1)进行位相加要加一次,还有一次是后面的溢出也需要加一次。0 1 1=0,1 1 1=1。
因此,n&(n-1也)是一样拿到了红色框1左边的原封不动的数,和正数也是同样的原理。
(补码的形式,-1也用8位代替)
总结一下,这个方法对于正负数都是一样的,以补码右边开始第一个1位基准,(n-1)保持该1的左边原封不动,右边取反,再&按位与运算,count 。新的n进行判断,n !=0 则代表里面还有1,n==0则代表n里面没有1,结束循环。
这个方法对于一个int类型的数,不需要32次循环执行,补码里面有几个1就执行几次循环!!!