C++如何判断一个数的二进制中1的个数

在面试和笔试中这是一种常见的题型,主要有三种方法进行实现:

1. 右位移

右位移的过程就是不断地将二进制数的最低位丢弃,然后最高位用1代替的过程。

右位移的基本思路为:先将要判断的数转换为正整数,然后不断地进行右位移,并将最后一位与1进行与运算,如果最后一位为1则与运算结果为1,就可以判断得到二进制中的一个i,直到二进制中所有的数都右移被丢弃,得到0循环结束。

int CountOneFunc1(int num)
{
	num = abs(num);
	int count = 0;
	// 当num未到达0时判断每一位的值是否为0
	while (num)
	{
		if (num & 1)
		{
			count++;
		}
		num = num >> 1;
	}
	if (num < 0)
	{
		count++;
	}
	return count;
}

需要注意的是,这种方法在一开始的时候要将传入的数据取得其绝对值,否则的话如果数值为负,每向右移动一位,高位会自动补1,就会导致死循环。


2. 左位移

左位移的思想与右位移类似,左位移的过程是不断地将二进制数的最高位丢弃,然后最低位用0代替的过程。由于在32位环境下,二进制有32个0或1,当最后一位从左侧被移除后,数值就会变回到0,循环结束。

左位移求1的过程是,首先设一个数为1,不断地使用这个数与目标整数做与运算,运算完成得到结果后将这个数左移一位,一直到这个数回归为0,循环结束,这样就能使用一个1与目标数字的每一位进行一个与运算,并记录其1的个数。

int CountOneFunc2(int num)
{
	num = abs(num);
	int count = 0;
	unsigned int flag = 1;
	while (flag)
	{
		if (num & flag)
		{
			count++;
		}
		flag = flag << 1;
	}

	return count;
}

可以发现上边的两种方法都将传入的数据进行了绝对值运算。右位移这么做的原因已经写明了,二左位移其实是可以不取绝对值的,因为负数在二进制是以补码的格式保存的,例如-1为32个1,也可以使用左位移的方法求1的个数,不会发生与右位移同样的死循环的错误。

第三种方法为与运算,我们知道将一个整数减去1之后,其对应的二进制中最右边的一个1会变为0,若其后存在0,则其之后的所有0都会变为1。基于此,设一个整数为n,则 n & (n-1)之后,会消掉n对应的二进制的最右边的1。因此,将一个数中所有1消掉所用的次数,即为该整数对应的二进制中1的个数。

int CountOneFunc3(int num)
{
	int count = 0;
	num = abs(num);
	while (num)
	{
		count++;
		num = num & (num - 1);
	}
	return count;
}

左移位或者是右移位,while循环执行的次数取决于计算机的位数,32位的计算机就执行32次,而64位则要执行64次。而与运算的方法执行的次数,只与该数中1的个数有关。所以与运算方法是三种方法中最好的一个,可以防止无意义操作的发生。

你可能感兴趣的:(C++)