c语言初阶关于二进制位的三道经典例题

本文介绍了三种计算一个整数二进制表示中1的个数的方法,包括通过右移和按位与操作,以及使用等式n=n&(n-1)。此外,还展示了如何分别打印出二进制数的奇数位和偶数位,以及如何找出两个数之间二进制位的不同数量。所有方法都基于位操作,适用于理解和优化二进制算法。
摘要由CSDN通过智能技术生成

目录

一.计算二进制位中1的个数

 思路1

代码实现

思路2

代码实现

思路3

代码实现

二.打印出一个二进制数的奇数位和偶数位上的数

思路

代码实现

三.打印出两个数中有几个二进制位的数不同

思路

代码实现


一.计算二进制位中1的个数

 

 思路1


通过% 2  和 / 2 可以得到二进制的每一位
 15 的二进制为   0000 1111  (前面的0暂且忽略,但我们要知道在x86下一个数的二进制位有32位)
 15 % 2 == 7……1 取 1  这个1可以看作是二进制位最小位上的1 (因为二进制位最小位权重为2^0  它为1的时候就表示
 十进制中的1  
 15 / 2 == 7 》》 0000 0111
  7 % 2 == 3……1  --取1   
  7 / 2 == 3  》》 0000 0011
  3 % 2 == 1……1  取1
  3 / 2 == 1  》》 0000 0001
  1 % 2 == 0……1  取1
   1/ 2 == 0     此时停止计算。也就是说,计算的数为0时停止,因此可以考虑将这个数当作进入循环的条件


此时我们可以将这个函数先写出来


int num_count(int n) 
{
	int count = 0;
	while (n)   
	{
		if (n % 2 == 1) //这个二进制位上为1
			count++;
		n /= 2;
	}
	return count;
}

但是这么写有个问题,就是输入-1的时候得到的是0.
负数时内存中保存的是补码,所以负数中的二进制位的1的个数,看的是它的补码
如 -1 》》 补码为  11111111111111111111111111111111  有32个1
 %2的时候也不等于1,count不++,且 -1 /2 的时候不够商,得0 ,无法进入下一次循环
但是如果把-1传参的时候,传为无符号类型的,那这个11111111111111111111111111111111  就相当于一个很大的正数
原本的符号位(二级制位最左边那一位)也表示一个真实的数  ,这时就没有了原反补码概念,可以直接计算

代码实现


#include <stdio.h>

int num_count(unsigned int n)  //这里定为unsigned int 是为了计算负数
{
	int count = 0;
	while (n)   
	{
		if (n % 2 == 1) //这个二进制位上为1
			count++;
		n /= 2;
	}
	return count;
}

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

思路2

当传的参数固定是int型时,也可以用按位与操作来解决
 &  两个操作数对应的二进制位有0则为0,同时为1才是1
用 一个数 & 1   (1的二进制位为0000000000000000000000000000000001) 这样就可以判断这个数的最低位是否为1

接着用右移操作符 >>

每次移动一个二进制位,最左边补一个0.最右边的二进制位舍去


判断完一次进行右移 >>  将原本的第二位二进制数放到第一位

 

遍历32次,就可以得出这个数的二进制位有多少个1了。

但这同时也是这个方法的缺点,每一次的计算都要遍历32次,时间效率非常低

代码实现

#include <stdio.h>

int num_count(int n)  
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if ((n >> i) & 1 == 1)    //注意这里进行右移操作对n原本的值不造成影响,所以可以重复利用
			count++;             //但是也要注意,此时右移的距离就要根据循环来改变,才能成功遍历二进制位
	}
	return count;
}

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

思路3

神奇的等式 n = n&(n-1)

假设输入n=14

              二进制
n             1110          原本的数的二进制位上有3个1
n-1          1101          每次计算都会去掉最右边的一个1
n& (n-1)  1100          最后得到0

n             1100
n-1          1011
n& (n-1)  1000

n             1000
n-1          0111
n& (n-1)  0000      

如果把n放进while循环  就可以实现,有多少个1,就执行多少次(最后一次是n为0的时候)

代码实现


#include <stdio.h>

int num_count(int n)
{
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	
	return count;
}

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

扩展:判断一个数n是不是2的幂次方
2的幂次方的里面二进制数只有1个1
当 n= n&(n-1)只执行一次的时候,这个数就是二的幂次方

二.打印出一个二进制数的奇数位和偶数位上的数

要求:获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列

思路

预期输出结果,一行输出偶数位上的二进制数,一行输出奇数位上的二进制数

 

我们只需要利用>>这个右移操作符,在每次移动的时候移动2位即可实现取得都是偶数位的二进制位和都是奇数位的二进制位。

再将偶数位和奇数位的打印分别放到两个for循环中

注意要得到的二进制数是从左到右得到的,所以循环初始值i从31 和30开始

右移31位就得到二进制最左边的二进制位(也就是第32位),右移30位就得到二进制数从左到右的第二个数(也就是第31位)

(二进制位的最右边那一位才是最低位哦)

而打印偶数位的限制条件设为 i>0  也就是 i 最小为1 ,右移1位,得到的就是原本二进制位中的第二位。

打印奇数限制条件则位 i >= 0 ,也就是 i 最小为0. 最后一次循环,右移0位,也就是得到原本的第一位。

这里的得到第几位都是用的&1操作,和上面那题原理相同。 

代码实现

#include <stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	int i = 0;
	for (i = 31; i > 0; i-=2)   //偶数位
	{
		printf("%d ", (n >> i) & 1);
	}
	printf("\n");
	for (i = 30; i >= 0; i -= 2)   //奇数位
	{
		printf("%d ", (n >> i) & 1);
	}
	return 0;
}

三.打印出两个数中有几个二进制位的数不同

 

 

思路

求两个二进制位有多少个位不同,可以利用操作符 按位异或^  (在键盘上是shift + 数字6)

按位异或的两个操作数的二进制相同时 为0,不同时为1

问题也就转换成,计算进行完按位异或的二进制数中有多少个1了

这时我们就又可以用到神奇的式子n=n&(n-1)来进行计算

代码实现

#include <stdio.h>

int print(int s)
{
	int count = 0;
	while (s)
	{
		s = (s - 1) & s;
		count++;
	}
	return count;

}

int main()
{
	int n, m;
	scanf("%d %d", &n, &m);
	int ret = print(n^m);   //此时 n^m得到的数就保存了n和m中有多少个不同的值
	printf("%d", ret);     //因为按位异或  相同为0,不同为1
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值