一共介绍3种方法:
方法1:类似于前一篇文章中求一个数的每一位,不过上一篇是按十进制来的,简单来说就是 %10后 /10,求二进制的话类比,先%2后/2见下图
你可能会写下面的代码:
int count_num_of_1(int num)
{
int count = 0;
while (num)
{
if (num % 2 == 1) // -1 % 2 = 0 count不自加
{
count++;
}
num = num / 2; // -1 / 2 = 0 , num = 0 ,下一次不进入循环,直接返回0
}
return count;
}
#include<stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int n = count_num_of_1(num);
printf("n = %d\n", n);
return 0;
}
当然上面的代码求正数可以,求负数会出现错误,以-1为例,-1进入循环后,-1%2!=1,count不自加,直接执行-1/2=0,下一次就不进入循环了,直接输出count=0;改正的话将函数的形参改为unsigned int型,用无符号的整形去接收时,-1会被看成一个很大的正数,结果就正确了
正确代码如下:
int count_num_of_1(unsigned int num) // -1会被当成一个很大的正数
{
int count = 0;
while (num)
{
if (num % 2 == 1)
{
count++;
}
num = num / 2;
}
return count;
}
#include<stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int n = count_num_of_1(num);
printf("n = %d\n", n);
return 0;
}
法2:用位操作符来实现
以3为例:
第一次
3:00000000000000000000000000000011
1:00000000000000000000000000000001
第二次
3>>1: 00000000000000000000000000000011
1: 00000000000000000000000000000001
判断32位
3&1==1就说明从右至左第一位上是一,如果要判断第二位上是不是1,只需将3>>1,再次&即可,从而判断出3的二进制中有2个1.
简单的总结一下就是:用一个数&1,得到结果是1则这个数的二进制这一位上是1,然后将这个数>>再&1,一共判断32位
代码如下:
int count_num_of_1(unsigned int num)
{
int count = 0;
int i = 0;
for (i = 0; i < 32; i++)
{
if (((num >> i) & 1) == 1)
{
count++;
}
}
return count;
}
#include<stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int n = count_num_of_1(num);
printf("n = %d\n", n);
return 0;
}
法3:先说一下法1和法2的缺点,判断次数过多,代码效率太低。下面介绍一种奇妙的方法
以15为例:
n&(n-1)
n=15
1111 n
1110 n-1
1110 n
1101 n-1
1100 n
1011 n-1
1000 n
0111 n-1
0000 结束
奇妙在于每一次n&(n-1)都会剥离下一个1
代码如下:
int count_num_of_1(int num)
{
int count = 0;
while(num)
{
num = num & (num - 1);
count++;
}
return count;
}
#include<stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
int n = count_num_of_1(num);
printf("n = %d\n", n);
return 0;
}
当然n&(n-1)还有其他的作用:
1 判断一个数是不是2的n次方:
2^1
10
2^2
100
2^3
1000
观察发现2的n次方的特点是二进制中只有一个1,直接n&(n-1),如果等于0则是2的n次方
代码如下:
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
if ((n & (n - 1)) == 0)
{
printf("是2的n次方\n");
}
return 0;
}
2 求两个数二进制中不同位的个数
常规思路就是上面法2,两个数>>后&1,后比较结果是否为1
奇妙的思路:现将两个数^, ^后相同的为0,不同的为1,后直接计算^得到的这个数二进制中1的个数
代码如下:
int count_diff_bit(int m, int n)
{
int count = 0;
//异或操作符
//相同为0,相异为1
int ret = m ^ n;
//统计一下ret中二进制位有几个1
while (ret)
{
ret = ret & (ret - 1);
count++;
}
return count;
}
#include<stdio.h>
int main()
{
int m = 0;
int n = 0;
scanf("%d %d", &m, &n);
int ret = count_diff_bit(m, n);
printf("%d\n", ret);
return 0;
}
后再接一个简单的小题,如何求一个数的二进制中的奇数序列和偶数序列。
思路如下:
最高的奇数位是第31位,现将这个数>>30去&1判断,后>>28,循环下去,每次>>数字减2且>=0
最高的偶数位是第32位,现将这个数>>31去&1判断,后>>29,循环下去,每次>>数字减2且>=1
代码如下:
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int i = 0;
//获取偶数位的数字
printf("偶数序列:");
for (i = 31; i>=1; i -= 2)
{
if (((n & (1 >> i)) == (1 >> i)))
{
printf("%d",((n>>i)&1));
}
}
printf("\n");
//获取奇数位的数字
printf("奇数序列:");
for (i = 30; i >=0; i -= 2)
{
if (((n & (1 >> i)) == (1 >> i)))
{
printf("%d", ((n >> i) & 1));
}
}
printf("\n");
return 0;
}