写一个函数求某个数对应的二进制中1的个数(牛客)

[该题的牛客链接](https://www.nowcoder.com/questionTerminal/8ee967e43c2c4ec193b040ea7fbb10b8?
在这里插入图片描述


一、方法一:%/达到二进制位右移的效果

//方法一
//         
//10 % 2 -> 0
//10 /= 2 -> 5 -> 101
// 
// 5 % 2 -> 1 -> count+1
// 5 /= 2 -> 2 -> 10
// 
// 2 % 2 -> 0
// 2 /= 2 -> 1 -> 1
// 
// 1 % 2 -> 1 -> count+1
// 1 /= 2-> 0 -> 跳出循环
//count得到就是2 
#include<stdio.h>

int Count(int n)
{
	int count = 0;
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n /= 2;
	}
	return count;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = Count(n);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述
代码上方注释也给出了大概解释,这里我再用文字解释一下。
我们拿10举例,10的二进制是1010,共两个1
首先10%2得到结果为0,不做处理
然后再让10/=上2,得到结果为5,此时我们可以注意到,5的二进制是101
然后再让5%2,得到结果是1,此时让count加1
然后再让5/=2,得到结果为2,二的二进制是10
然后可以让2在%2得到结果为0,同样不做处理
然后2/=2得到结果为1,对应二进制就是1
然后1%2,结果是1,让count在加1
然后1/=2结果是0,最终跳出循环
我们可以发现,每次/=得到的二进制数从1010到101再到10最好到1,有种类似二进制数不断右移的效果,每次右移之后再判断末位是否为1就行,因此同样除了使用%/方法外,我们可以直接用>>来实现

1.1用>>操作符实现

//就是把/= 换成>>=,只是用/=更好理解一些
#include<stdio.h>

int Count(int n)
{
	int count = 0;
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n >>= 1;
	}
	return count;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = Count(n);
	printf("%d\n", ret);
	return 0;
}

1.2方法一代码的改进(针对负数情况)

回到最开始的代码,当我们输入负数时

会发现结果竟然是0,但是-1的补码是32个1呀,结果应该是32才对
那我们再来分析一下
-1 % 2 等于-1,count还是0
然后-1 /= 2等于 0,跳出循环,所以结果最终是0
改进代码:

#include<stdio.h>

int Count(unsigned int n)
{
	int count = 0;
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n /= 2;
	}
	return count;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = Count(n);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述
分析:当形参用int时,-1传进去就还是-1,因为看作有符号数,就把-1的补码再转回源码得到-1
而当用unsigned int时,-1的补码就被当作无符号数了,所以转成源码就还是32个1了,可以正常计算1的个数了
但是但是,牛客的题目是接口类型的,形参类型已经给定是int了,所以这个代码还是无法通过

二、方法二:按位与1(&1)来判断最末位是否是1

#include<stdio.h>

int Count(unsigned int n)
{
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if (1 == ((n >> i) & 1))
		{
			count++;
		}
	}
	return count;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = Count(n);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述
当然-1也是可以的
在这里插入图片描述
分析:

在这里插入图片描述
当一个数对应的二进制数补码末位为1时,该数&1结果为1,末位为0时结果为0
总结一个数&1能得到该二进制的最末位,该数|0(或上0)也可以达到该效果。

三、方法三:n = n&(n-1)——比较难想到的方法

首先,我们看看方法二里面的不足:不管二进制数中有几个1,循环都要执行32次,严重影响效率
改进:n = n&(n-1)
在这里插入图片描述
每个红框就是执行一次n = n&(n-1),当最终执行结果为0时停止,执行几次就代表最开始的n的二进制中有几个1
原理:每次的减一操作,能够将二进制最靠右的1给减掉,因为这个1的右边全是0,减一需要借位了,刚好把1给借掉,可以导致n和n-1的1、0错位,再&之后就全是0了,这样一套操作就能消除一个零
这个方法就能避免方法二的缺点,当计算1对应二进制位1的个数时只会执行一次!

拓展1——判断一个数是不是2的幂次方(非负整数)

常规解法不写,写一个通过方法三拓展出来的方法
这题的关键是找到2的幂次方数的特点:对应的二进制数中1的个数为1,例如1——1,2——10,4——100,8——1000…所以只要n&(n-1)的结果等于0,就代表者只要一个1!

#include<stdio.h>

int Power(int n)
{
	return !(n & (n - 1));
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	if (Power(n))
	{
		printf("%d是2的幂次方", n);
	}
	else
	{
		printf("%d不是2的幂次方", n);
	}
	return 0;
}

在这里插入图片描述
在这里插入图片描述

拓展2——就两个数二进制位有多少个位不同

编程实现:两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?
输入例子:
1999 2299
输出例子:7

/*
思路:
1. 先将m和n进行按位异或,此时m和n相同的二进制比特位清零,不同的二进制比特位为1
2. 统计异或完成后结果的二进制比特位中有多少个1即可
*/
#include <stdio.h>
int calc_diff_bit(int m, int n)
{
	int tmp = m^n;
	int count = 0;
	while(tmp)
	{
		tmp = tmp&(tmp-1);
		count++;
	}
	return count;
}


int main()
{
 int m,n;
 while(scanf("%d %d", &m, &n) == 2)
 {
     printf("%d\n", calc_diff_bit(m, n));
 }
 return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值