15 二进制中1的个数
题目描述:
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。例:表示成二进制是1001,有两位是1,因此函数返回2。
测试用例:
1)正数(包括边界值:1、0x7FFFFFFF(除了首位为0,其他都为1,最大的正数))
2)负数(包括边界值 0x80000000(只有首位是1,其他位为0,”最小的负数“)、0xFFFFFFFF(所有位全是1,指-1))
3)0
解题思路:
1)常规解法:
n与二进制1相与,可以判断n的最低位是不是为1。接着把1左移1位变成2,再和n相与,能知道n的次低位是不是为1。。。
//int 32 bit class Solution { public: int NumberOf1(int n) { int count = 0; unsigned int flag = 1; while(flag){ if(n&flag) count++; flag = flag<<1; } return count; } };
循环的次数等于正数二进制的位数(int 32bit)
2)欣赏的解法
规律:把一个整数减去1(就是把最右边的1变成0,如1100-1=1011),再和原整数做与运算,会把该整数的最右边的1变成0(1100&1011=1000)。那么一个整数的二进制表示中有多少个1,就可以进行多少个这样的操作。
class Solution { public: int NumberOf1(int n) { int count = 0; while(n){ //n!=0时(正数/负数),二进制中一定含有1. count++; n = n&(n-1); //每运行一次,二进制中减少一个1 } return count; } };
3)使用右移运算,错误代码:当n是负数时,会陷入无限循环
class Solution { public: int NumberOf1(int n) { int count = 0; while(n){ if(n&1) count++; n = n>>1; //当n为负数时,右移,高位用1补位,最后终所有位都是1(0xFFFFFFFF)陷入死循环 } return count; } };
修改,对负数另做运算:负数首位一定是1,count计数加1。将其变为0,右移补位为0,同正数一样。
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
if(n < 0){ //n为负数,把首位的1变0,count加1。剩下的按正数处理
n = n & 0x7FFFFFFF;
++count; //n不为0,一定含有1
}
while(n != 0){
count += n & 1;
n = n >> 1;
}
return count;
}
};
4)使用内建函数
class Solution {
public:
int NumberOf1(int n) {
return bitset<32>(n).count();
}
};//
-- bitset存储二进制数位,bitset中的一个元素一般只占1 bit。
-- bitset中的每个元素都能单独被访问,例如对于一个叫做foo的bitset,表达式foo[3]访问了它的第4个元素,就像数组一样。
-- 整数类型和布尔数组都能转化成bitset。
-- bitset的大小在编译时就需要确定。如果你想要不确定长度的bitset,请使用 vector<bool>。
-- bitset的运算就像一个普通的整数一样,可以进行与(&)、或(|)、异或(^)、左移(<<)、右移(>>)等操作。
https://www.cnblogs.com/RabbitHu/p/bitset.html
https://www.cnblogs.com/magisk/p/8809922.html
基础知识:
[1] 位运算有5种操作:与&、或|、异或^、左移<<、右移>>。
左移 m<<n :最左边的n位将被抛弃,同时在最右边补上n个0。
右移 m>>n:
-- 如果数字是无符号数值,用0填补左边的n位
-- 如果数字是有符号数值,则用数字的符号位填补最左边的n位
正数:右移,左侧补n个0
负数:右移,左侧补n个1
[2] 右移1位相当于除以2运算,因为除法的效率比移位运算要低的多,在实际的编程中应该尽可能的用移位代替除法。
[3] 因为右移对正负的补位不同,在编写程序时尽量使用左移。