除法运算效率较低,所以在编程中,尽可能地用移位来代替除法。
这个题目中的
class Solution {
public:
int NumberOf1_dead(int num)
{
if(num == 0) return 0;
int cnt = 0;
while(num >= 1)
{
//if(num % 2 != 0) 比较低效,用位运算
if(num & 1 != 0)
cnt ++;
// num /= 2;低效
num = num >> 1;
}
}
int NumberOf1_flag(int num)
{
if(num == 0) return 0;
int cnt = 0;
unsigned int flag = 1; //这里使用unsigned int 类型
//while(flag >= 1) 会存在flag为负的情况,
while(flag >= 1)
{
//if(num & flag != 0) 这里不能这么写,&的优先级低于 !=
if((num & flag) != 0)
cnt ++;
flag = flag << 1;
}
return cnt;
}
int NumberOf1(int num)
{
if(num == 0) return 0;
int cnt = 0;
while(num)
{
cnt ++;
num = (num - 1) & num;
}
return cnt;
}
};
这里的判断不能写为if(num & flag != 0)
,因为&
的优先级低于!=
。
这里num = (num - 1) & num;
的思路会带来惊喜,但不容易想到。
num-1相当于
- 当最低位是1,
num-1
就相当于去反操作,num由1变成了0,之前的所有位都保持不变; - 当最低位是0,假设最右边的1位于第m位,
num-1
后,第m位由1变成0,而第m位之后所有0都变成了1,而第m位之前的所有位都保持不变。
通过上面两种情况分析,获知,num-1
都是把最右边的1变成0,而左边的都保持不变。
所以,num & (num-1)
相当于把 num
最右边的1变成0。
eg: 判断n = 1100有几个1
n = 1100 & (1100-1) = 1100 & 1011 = 1000
n = 1000 & (1000-1) = 0111 & 1000 = 0
两次循环就结束了,速度非常快。
时间复杂度:O(n) n为val中1的个数
空间复杂度:O(1)
num & (num-1)
相当于把 num
最右边的1变成0。,很多二进制问题都可以用这个思路解决。
eg:
- 一个整数是不是2的整数次方
- 输入两个整数m、n,赍需要改变m的二进制表示中的多少位才能得到n
- 1.求这两个数的异或
- 2.统计异或结果中1的位数