0
点赞
收藏
分享

微信扫一扫

vue3 vue.config.js配置Element-plus组件和Icon图标实现按需自动引入

_鱼与渔_ 2023-05-04 阅读 34

在这里插入图片描述

本篇博客会讲解一道经典的题目:求一个整数二进制中1的个数。阅读本篇博客前,需要你对C语言如何进行二进制位操作有一定的了解,如果还不太了解的话,可以阅读一下我的这篇博客。

我们假设有一个int类型的整数n,我们知道,n在内存中是以二进制的补码的形式存储的。这个补码的二进制,是由0和1组成的,究竟有多少个1呢?我们能不能写个程序来求一下?

“&1”的妙用

如果你认真读了我讲解位操作符的那篇博客,应该知道n&1代表了啥。不知道也没关系,可以来观察一下:

1: 00000000000000000000000000000001
n: 11000111101011101010011110110111

左边的31位,由于“任何数 & 0”都会得到0,所以最终的结果只由最右边的1位决定。分类讨论一下:

  1. 如果n的最右边1位是0,则按位与的结果是0。
  2. 如果n的最右边1位是1,则按位与的结果是1。

再提炼一下,n & 1的结果是1,说明n的最右边1位是1,否则n的最右边1位是0,换句话说,n & 1的结果是n最右边的1位。

那么我们就有思路了:我们通过(n >> i) & 1就能拿到第i位的值,统计其中有几个1就行了。

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

从“%10 /10”到“%2 /2”

看到“%10 /10”,大家会联想到啥?如果你做题做多了,会第一时间反应到:%10可以拿到一个十进制数的最后一位,而/10会把最后一位去掉!比如:

123 % 10 = 3
123 / 10 = 12
12 % 10 = 2
12 / 10 = 1
1 % 10 = 1
1 / 10 = 0

在n变成0之前,就可以拿到十进制中的每一位。那如果是拿到二进制中的每一位呢?“%2 /2”不就行了!不过需要注意的是,“%2 /2”的操作必须对无符号整数进行,如果对有符号整数,遇到负数时结果是错的。比如,-1的补码有32个1,但是使用“%2/2”操作并不会操作32次,第一次/2就变成0了,结果是错的。

int GetNumOf1(unsigned int n)
{
	int count = 0;
	while (n)
	{
		if (n % 2 == 1)
			++count;

		n /= 2;
	}
	return count;
}

神奇的“n &= n - 1”

n &= n - 1的效果是去掉n的二进制位中最右边的那个1。比如:

n: 00000000000000000010001100001001
n &= n - 1
n: 00000000000000000010001100001000
n &= n - 1
n: 00000000000000000010001100000000
n &= n - 1
n: 00000000000000000010001000000000
n &= n - 1
n: 00000000000000000010000000000000
n &= n - 1
n: 00000000000000000000000000000000

由于一开始的二进制中有5个1,所以执行5次n &= n - 1后,n就变成0了。

这个思路实现成代码如下:

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

总结

  1. n & 1的操作可以得到n的补码的二进制最右边的那一位数。
  2. 反复“%2 /2”可以得到二进制中的每一位。
  3. n &= n - 1的效果是去掉n的二进制位中最右边的那个1。

感谢大家的阅读!

举报

相关推荐

0 条评论