本文參考自《劍指offer》一書,代碼采用Java語言。
題目
請實現一個函數,輸入一個整數,輸出該數二進制表示中1的個數。例如把9表示成二進制是1001,有2位是1。因此如果輸入9,該函數輸出2。
思路
遇到與二進制有關的題目,應該想到位運算(與、或、異或、左移、右移)。
方法一:”與運算“有一個性質:通過與對應位上為1,其余位為0的數進行與運算,可以某一整數指定位上的值。這道題中,先把整數n與1做與運算,判斷最低位是否為1;接着把1左移一位,與n做與運算,可以判斷次低位是否為1……反復左移,即可對每一個位置都進行判斷,從而可以獲得1的個數。這種方法需要循環判斷32次。
方法二(better):如果一個整數不為0,把這個整數減1,那么原來處在整數最右邊的1就會變為0,原來在1后面的所有的0都會變成1。其余所有位將不會受到影響。再把原來的整數和減去1之后的結果做與運算,從原來整數最右邊一個1那一位開始所有位都會變成0。因此,把一個整數減1,再和原來的整數做與運算,會把該整數最右邊的1變成0。這種方法,整數中有幾個1,就只需要循環判斷幾次。
測試用例
1.正數(包括邊界值1、0x7FFFFFFF)
2.負數(包括邊界值0x80000000、0xFFFFFFFF)
3.0
完整Java代碼
(含測試代碼)
/** * * @Description 面試題15:二進制中1的個數 * * @author yongh * @date 2018年9月17日 下午3:01:16 */ // 題目:請實現一個函數,輸入一個整數,輸出該數二進制表示中1的個數。例如 // 把9表示成二進制是1001,有2位是1。因此如果輸入9,該函數輸出2。 public class NumberOf1InBinary { public int NumberOf1_Solution1(int n) { int count = 0; int flag = 1; while (flag != 0) { if ((flag & n) != 0) count++; flag = flag << 1; } return count; } public int NumberOf1_Solution2(int n) { int count = 0; while (n != 0) { count++; n = (n - 1) & n; } return count; } // =========測試代碼========= void test(String testName, int n, int expected) { if (testName != null) System.out.println(testName + ":"); if (NumberOf1_Solution1(n) == expected) { System.out.print(" soluton1:" + "passed "); } else { System.out.print(" solution1:" + "failed "); } if (NumberOf1_Solution2(n) == expected) { System.out.println("soluton2:" + "passed "); } else { System.out.println("solution2:" + "failed "); } } void test1() { test("Test for 0", 0, 0); } void test2() { test("Test for 1", 1, 1); } void test3() { test("Test for 10", 10, 2); } void test4() { test("Test for 0x7FFFFFFF", 0x7FFFFFFF, 31); } void test5() { test("Test for 0xFFFFFFFF", 0xFFFFFFFF, 32); } void test6() { test("Test for 0x80000000", 0x80000000, 1); } public static void main(String[] args) { NumberOf1InBinary demo = new NumberOf1InBinary(); demo.test1(); demo.test2(); demo.test3(); demo.test4(); demo.test5(); demo.test6(); } }

Test for 0: soluton1:passed soluton2:passed Test for 1: soluton1:passed soluton2:passed Test for 10: soluton1:passed soluton2:passed Test for 0x7FFFFFFF: soluton1:passed soluton2:passed Test for 0xFFFFFFFF: soluton1:passed soluton2:passed Test for 0x80000000: soluton1:passed soluton2:passed
收獲
1.與二進制有關的題目要往位運算方面想,復習一下:二進制位運算的幾個用法
2.注意:負數右移還是負數!即如果對n=0x8000 0000右移,最高位的1是不會變的。如果這道題目通過令n=n>>1來計算n中1的個數,該數最終會變成0xFFFF FFFF而陷入死循環!
3.把一個整數減1,再和原來的整數做與運算,會把該整數最右邊的1變成0。這種方法一定要牢牢記住,很多情況下都可能用到,例如:
1)一句話判斷一個整數是否為2的整數次方;
2)對兩個整數m和n,計算需要改變m二進制表示中的幾位才能得到n。
4.與數字操作有關的題目,測試時注意邊界值的問題。對於32位數字,其正數的邊界值為1、0x7FFFFFFF,負數的邊界值為0x80000000、0xFFFFFFFF。
5.幾個細節問題
1)flag=flag<<1,而不是只寫一句flag<<1;
2)flag&n!=0,而非flag&n==1; 也就不能寫成count+=(flag&1)了
3)if語句中,不能寫為if(flag&n!=0) ,而要寫成 if((flag&n)!=0),需要注意一下