標籤:style class blog code color 問題
題目:請實現一個函數,屬於一個整數,輸出該數二進位表示中1的個數,例如把9表示成二進位是1001,有2位為1。因此如果輸入9,該函數輸出2。
看完題目,相信大家很快就能想到一個解題思路:先判斷整數二進位表示中最右邊的一位是否為1,接著把輸入的整數右移一位,此時原來處於從右邊起的第二位被移動至最右邊了,再判斷是不是1,這樣每次移動一位,直到這個整數變成0,即能夠得到整數二進位表示形式中1的個數,而現在問題變為如何判斷數位最後一位為1,其實這個也很簡單,只需要將數字與1做與運算,如果結果為1,則表示最後一位為1,否則為0。有了上面的思路,我們很快能寫出如下的代碼:
1 int NumberOf1(int number) 2 { 3 int count = 0; 4 5 while (0 != number) 6 { 7 if (0 != (number & 0x01)) 8 { 9 ++count;10 }11 12 number = number >> 1;13 } 14 15 return count;16 }
作為一個嚴謹的程式猿,寫完代碼必須要對代碼進行相應的測試,當我們輸入正整數時,程式能夠正確運行,但是當我們輸入負整數時,程式好像不能停止了,為什麼會這樣?當我們再回頭去看代碼,並看到number = number >> 1時,才恍然大悟,因為負數帶有符號位,而在進行右移操作時,由於要保持其任然為負數,會在左邊的第一位補1而不是0,這樣一直進行以為,最終整數變為了0XFFFFFFFF,而陷入死迴圈。如何避免該問題呢?
當上面的問題出現時,我們想到,既然不能將整數向右移動,那我們為什麼不將1向左邊移位後再與輸入整數進行與運算呢?這樣的處理方式能達到同樣的效果,並且左移永遠都只會在右邊的位置補0,這樣就避免了死迴圈的出現,好了,有了這樣的思路我們又寫出下面的第二個版本代碼:
1 int NumberOf1(int number) 2 { 3 int count = 0; 4 int flag = 0x01; 5 6 while (0 != flag) 7 { 8 if (0 != (number & flag)) 9 {10 ++count;11 }12 13 flag = flag << 1;14 }15 16 return count;17 }
當完成代碼之後,進行測試都能得到正確的結果,似乎已經是完美答案了,but,我們發現不管我們輸入的整數二進位形式中有幾個1,我們的程式都將迴圈32次才能得到結果,有沒有什麼方法能夠使我們的迴圈次數更少呢?比如二進位中有幾個1就只迴圈幾次的方法,答案是肯定的,請接著往下看。
再分析演算法之前,我們先來分析把一個整數減1的情況,如果一個整數不等於0,那麼該整數的二進位表示中至少有1位是1,先假設這個數的最後一位是1,將該整數減1之後,最後一位變為0,其他位保持不變,接下來假設最後一位不是1而是0的情況,如果該數最右邊的1位於第m位,則將此數減1之後,該數的第m位由1變為0,而m位之後的所有位都由0變為1,第m位之前的位都保持不變,如果再將該整數與該整數減1之後的結果進行與運算,其效果相當於把整數最右邊為1的位由1變為0。我們把上面的分析總結起來就是:把一個整數減1之後再與整數本身做與運算,會把整數二進位形式中最右邊的一個1變成0,那麼一個整數的二進位形式中存在多少個1,我們就可以對該整數做幾次上述的操作,有了上面的思路,我們能寫出新的解法:
1 int NumberOf1(int number) 2 { 3 int count = 0; 4 5 while (0 != number) 6 { 7 ++count; 8 9 number = number & (number - 1);10 }11 12 return count;13 }