Brian Kernighan's Algorithm
Brian Kernighan's Algorithm是一种高效计算一个整数二进制表示中1的个数的算法。它的基本思想是利用 x &= x-1
操作来消除一个整数 x
的二进制表示中最右边的1。
算法的步骤如下:
初始化一个计数器
count
为0。当
x
不等于0时,执行以下操作:执行
x &= x-1
,将x
中最右边的1及其右边的所有位都置为0。将计数器
count
加1。返回计数器
count
的值,即二进制表示中1的个数。
该算法的原理是,每次执行 x &= x-1
操作,都会消除 x
中最右边的1,并且仅有该位变为0,而其他位保持不变。通过重复执行这个操作直到 x
变为0,就能统计出二进制表示中1的个数。
相比于遍历所有的位数进行判断,Brian Kernighan's Algorithm的优势在于,它只需要执行与1的个数相等的循环次数,即执行次数等于二进制表示中1的个数。这使得该算法在时间复杂度上更加高效。
这个算法以Brian Kernighan的名字命名,因为他在1988年的《The C Programming Language》一书中介绍了这种方法,用于计算一个整数的二进制表示中1的个数。

在这个代码中,我们使用 num &= (num - 1)
的操作来清除 num
的二进制表示中最右边的一个1。通过重复执行这个操作直到 num
变为0,我们可以计算出二进制表示中1的个数。
每次执行 num &= (num - 1)
时,都会将 num
中最右边的1及其右边的所有位都置为0,因为 (num - 1)
将最右边的1变为0,而该1右边的所有位都变为1,然后与原来的 num
进行与运算,结果就是将最右边的1及其右边的所有位都置为0。重复这个操作直到 num
变为0,就能计算出二进制表示中1的个数。
这种方法的优势在于,对于有多少个1的问题,只需要执行相应数量的循环次数,而不是遍历所有的位数。这使得该方法在时间复杂度上更加高效。

在这个修改后的代码中,我们使用了一个无符号整数变量mask
来进行左移操作。循环中,我们检查num
与mask
的与运算结果,如果结果为非零,则说明对应位是1,将计数器加1。然后,将mask
左移1位,继续下一次循环。循环终止的条件是mask
变为0,即所有位都被检查过。
需要注意的是,为了避免负数左移导致的问题,我们将mask
声明为无符号整数。这样,在左移操作中,高位会自动补0,而不是补1。这样可以确保计数结果正确,无论输入的整数是正数还是负数。

在这个程序中,countOnes
函数接受一个整数作为输入,然后通过不断右移这个整数并检查最低位是否为1来计算1的个数。在每次循环中,使用位运算符&
来检查最低位是否为1,如果是则计数器加1。然后将整数右移1位,继续下一次循环,直到整数变为0为止。
在main
函数中,首先获取用户输入的整数,然后调用countOnes
函数计算二进制表示中1的个数,并将结果打印出来。
请注意,这个方法是一种常见的解决方案,但是对于负数,右移操作可能会引入高位补1的情况,导致计数结果不正确。如果需要计算负数的二进制表示中1的个数,可以使用位运算和循环来处理特殊情况。