题目:
请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如把9表示成二进制是1001,有2位是1。因此如果输入9,该函数输出2。
知识点:
这道题是考查位运算。位运算一共有5种:与、或、异或、左移和右移。
与运算特点:全真为真;或运算特点:全假为假;异或运算的特点:相同为假。
左移:m<<n,表示把m左移n位。左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0。
右移:m>>n,表示把m右移n位。右移n位的时候,最右边的n位将被丢弃。注:如果数字是一个无符号值,则用0填补最左边的n位。如果数字是一个有符号数值,则用数字的符号位填补最左边的n位。(符号位为0填补0,符号位为1填补1)。
第一想法:
先判断整数二进制表示中的最右边一位是不是1。接着把输入的整数右移一位,此时原来还处于右边数起的第二位被移到最右边了,再判断是不是1。这样每次移动一位,直到整个整数变成0为止。(判断是不是的条件是整数和1做位与运算看结果是不是0就知道了)。
实现代码:
public int numberOf1(int n){ int count = 0; while(n!=0){ if((n&1) != 0){ ++count; } n = n >> 1; } return count; }
但是从上面的右移规则的定义可以看出如果符号位为1时,这个程序会陷入死循环。故这个代码符合符号位不为1的题目。
改良思路:
为了避免死循环的发生,我们不右移数字n。我们左移1,即首先把n和1做位与运算,判断n的最低位是不是为1。接着把1左移一位得到2,再和n做与运算,就能判断n的次低位是不是1.....,这样反复左移,每次都能判断n的其中一位是不是1。
实现代码:
public int numberOfs1(int n){ int count = 0; int flag = 1; while(flag!=0){ if((n&flag) != 0){ ++count; } flag = flag << 1; } return count; }
优化思路:
把一个整数减去1,再和原来整数做与运算,会把该整数最右边一个1变成0,。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。
public int numbersOf1(int n){ int count = 0; while(n != 0){ ++count; n = (n-1) & n; } return count; }
小结:
考查对二进制及位运算的理解。以及思考问题的全面性。
拓展题:
题目:
用一条语句判断一个整数是不是2的整数次方。
思路:
一个整数如果是2的整数次方,那么它的二进制表示中有且只有一位是1,而其他所有位都是0。故把这个待测整数减去1后再和它自己做与运算,这个整数中唯一的1就会变成0。
代码:
public boolean judgeNum2Square(int num){ if((num & (num-1)) != 0){ return false; }else{ return true; } }
题目:
输入两个整数m和n,计算需要改变m的二进制表示中的多少位才能得到n。比如:10的二进制位1010,13的二进制位1101,1010改变3位才能变成1101。
思路:
第一步求这两个数的异或结果;第二步统计异或结果中1的位数。
代码:
public int changeCount(int n, int m){ int count = 0; int x = n^m; while(x!=0){ ++count; x = (x-1) & x; } return count; }
小思:
把一个整数减去1之后再和原来的整数做位与运算,得到的结果相当于是把整数的二进制表示中的最右边一个1变成0。这个方法在解决很多二进制问题都可以用。
小结:
基础很重要啊,充分理解位运算。