目录
0).提出问题
来自牛客网的一道题目:写一个函数返回参数二进制中1的个数。
1).第一种思路
基本原理
在十进制位当中,想提取一个整数的每一位数字可以采取反复%10再/10的方法,直到得到0停止,如下图所示,123经过反复%10 /10 分离出1 2 3。那对于二进制来说,是否可以用类似的方法来分离每一位数字呢?
假设我们需要分离15的每一位数字,我们知道15在二进制中表示位00000000/00000000/00000000/00001111,这里我们将前面省略取后面八个数来表示15.
因为是二进制,所以我们需要%2/2来进行操作,我们可以发现每进行一次%2/2依然可以提取出每位数字,了解了基本原理,我们就可以做题啦!
解决方法:
题目给出了主函数部分,这里直接引用
int main()
{
int n = 0;
scanf("%d", &n);
int ret = NumberOf1(n);//需要实现的函数
printf("ret = %d", ret);
return 0;
}
此时我们用以下代码实现while循环和if语句来计算1的数量。
int NumberOf1(int n)
{
int count = 0;//初始化计数器
while (n)//当n不等于0时进入循环
{
if (n % 2 == 1)
{
count++;//当%2后得出1即分离出的数字为1计数器加1
}
n = n / 2;
}
return count;
}
15的二进制位为00001111,结果为4代码正确,然而输入负数后代码就会运行错误,该如何优化代码?
优化代码
****-1的原码是10000000/00000000/00000000/00000001,按位取反得到反码后为11111111/11111111/11111111/11111110,反码加一得到补码为11111111/11111111/11111111/11111111,理应得到ret=32,程序是哪里出问题了呢?
当n=-1时,进入循环后%2余-1,-1/2为0,循环结束,count为0
解决方法:unsigned 修饰 int n,这是-1会被认为是一个超级大的整数,没有符号概念,得出正确答案32
int NumberOf1(unsigned int n)
{
int count = 0;//初始化计数器
while (n)//当n不等于0时进入循环
{
if (n % 2 == 1)
{
count++;//当%2后得出1即分离出的数字为1计数器加1
}
n = n / 2;
}
return count;
}
2).第二种思路
基本原理
在二进制位中,想分离出某位数字,只要给这位数字按位与1即可,如果一个数按位与1结果位1,那么这个数的最低位一定为1,如果想往前计算数字,可以用位运算符解决,二进制中有32位数字,则循环32次
解决方法
int NumberOf1(int n)
{
int count = 0;//初始化计数器
int i = 0;
for (i = 0; i < 32; i++)
{
if (((n >> i) & 1) == 1)//右位移运算符循环32次使每一位数字都按位与1,得到1则计数器+1
{
count++;
}
}
return count;
}
得出正确答案,实现代码
3).第三种思路
基本原理
一个巧妙的方法,我们让n&(n-1),假设n=15
解决方法
int NumberOf1(int n)
{
int count = 0;//初始化计数器
int i = 0;
while (n)
{
n = n & (n - 1);
count++;
}
return count;
}
得到正确答案