劍指Offer-其他-(1)
知識點:位運算和二進位制。
題目描述
輸入一個整數,輸出該數二進位制表示中1的個數。其中負數用補碼錶示。
1:什麼是位運算
位運算是把數字用二進位制表示之後,對每一位上0或1的運算。二進位制及其位運算是現代電腦科學的基石,很多底層的的技術都離不開位運算 。
與:&(有0為0);
或:|(有1為1) ;
異或:^(不同為1) ;
**左移運算子:**m<<n(把m左移n位,最左邊的n位將被丟棄,右邊補上n個0);
**右移運算子:**m>>n(把m右移n位,最右邊n位將被丟棄:如果數字是一個無符號整數,則用0填補最左邊的n位;如果 數字是一個有符號的數值,則用數字的符號位填補最左邊的第n位。也就是說數字原先是一個正數,則右移之後在左邊補n個0,如果數字原先是負數,則右移動之後在左邊補n個1.例如:00001010>>2=00000010 /10001010>>>3 11110001)
2:Java中的整數型別
在Java中,整數資料型別可以分為long,int,short以及byte型別;
long(長整型)為64位,也就是8個位元組(bytes)可以表示的範圍是:-2 ^63 (-9223372036854775808)~2 ^63-1(9223372036854775807);
int為32位,也就是4個位元組(bytes),可以表示的範圍是-2 ^31(-2147483648)~ 2 ^31-1(2147483647);
short(短整型)為16位,也就是兩個位元組(bytes),可以表示的範圍是(-32768~32767);
byte型別(位元組型別),也就是一個位元組(bytes),可以表示的範圍是(-128~127);
3:計算機中的整數的儲存
計算機中帶符號的整數採用二進位制的補碼進行儲存
正數:原碼,反碼,補碼都是一致的;
負數:(觀察補碼的轉換,題目中說負數是以補碼的形式存在)
/一個整數按照絕對值大小轉換成的二進位制數,是為原碼。
5的原碼:00000101。/
反碼是和原碼反著來的。
反碼加一叫補碼。
補碼就是負數在計算機中的二進位制表示方法。那麼,11111011表示8位的-5;
如果要表示16位的-5 ,在左邊添上8個1即可。
4:分析題目《劍指Offer》
基本思路:先判斷整數二進位制表示中最右邊一位是不是1;接著把輸入的整數右移一位,再判斷是不是1;這樣直到整個整數變為0為止。(判斷整數的最右邊是不是1需要把整數和1做位於運算
public class Solution{
public int NumberOf1(int n){
int count=0;
while(n!=0){
if((n&1)==1)
{count++;}
n=n>>1;
}
return count;
}
}
上面的程式碼有一個問題:如果那個數字是一個負數。如0x80000000右移一位,不是變成了0x40000000,而是變成了0xC0000000(因為高位補1了)。所以移位後的最高位會被設定為1.那麼最終這個數字會變成0xFFFFFFF而陷入死迴圈。
把整數右移一位和把整數除以2在數學上是等價的,但是除法的效率比位運算低得多,在實際的程式設計中應儘可能的使用移位運算代替除法
3:下面是通過的程式碼
針對上述的問題,我們可以補右移數字n。首先把n和1做位於運算,判斷n的最低是不是1.接著把1左移一位得到2,再和n做與運算,,,,,反覆左移;在這個解法中迴圈的次數是32次(相當於整數二進位制的位數)
程式碼如下面
public class Solution{
public int NumberOf1(int n){
int count=0;
int flag=1;
while(flag!=0){
if((n&flag)==flag){count++;}
flag=flag<<1;
}
return count;
}
}
下面是一個驚喜的做法:
1:先分析一個問題(一個整數減去1)
如果一個整數不等於0,那麼該整數的二進位制表示至少有一位是1:如果這個數的最右邊是1,那麼減去1之後,最後一位變成0而其他所有位都保持不變,也就是最後一位相當於做了取反操作,由1變成了0;如果最後一位不是1是0,假設該整數二進位制表示中最右邊的1位於第m位,那麼減去1的時候,第m位由1變成了0,第m位之後的數字都變成了1,第m位前面的數字都不變。
從上面的案例啟發:把一個整數減去1,再和原整數進行與運算,會把該整數最右邊的1變成0;那麼一個整數的二進位制表示中有多少個1,就可以進行多少次這樣的操作。基於這種思路,寫出如下程式碼:
public class Solution{
public int NumberOf1(int n){
int count=0;
while(n!=0){
count++;
n=n&(n-1);
}
return count;
}
}
觀察下圖:-5的補碼(以byte型別儲存)有
最終除錯程式碼如下:
package 一個整數二進位制數中1的位數;
public class Solution{
public static int NumberOf1(int n){
int count=0;
while(n!=0){
count++;
n=n&(n-1);
}
return count;
}
}
下面是測試程式碼:
package 一個整數二進位制數中1的位數;
public class Test {
public static void main(String[] args) {
int flag1 = Solution.NumberOf1(5);
System.out.println(flag1);
int flag2 = Solution.NumberOf1(0);
System.out.println(flag2);
int flag3 = Solution.NumberOf1(-5);
System.out.println(flag3);
}
}
下面是執行結果,計算機預設的是32位,也就是long型別的資料。(徹徹底底的搞懂了啊!!!!!!!!不將就!!!!!)