1. 程式人生 > 其它 >劍指offer-java進位制與數位運算子

劍指offer-java進位制與數位運算子

技術標籤:資料結構和演算法演算法字串java演算法程式語言

今天刷題遇到了二進位制操作,發現我以前學的忘得差不多了。 甚至有些java運算子號,我完全不清楚的,特地記錄一下。

文章目錄


前言

java程式設計師只有10種人,一種懂二進位制,一種不懂。呵呵~好冷。

一、java的進製表示

  • 二進位制: 0b_____, 使用0b開頭
  • 八進位制: 0_____, 使用0開頭【對,如果開頭有0就是八進位制,沒有0就是十進位制,簡單粗暴】
  • 十六進位制:0x_____, 使用0x開頭。

二、java進位制轉換方法

1.其他進位制轉化為二進位制字串

這裡說的轉化基本都是轉為其他進位制字串或者其他進位制字串轉化十進位制數值。
PS:進位制的英文: Radix

數值轉其他進位制:

  • 轉二進位制:Integer.toBinaryString(i);
  • 轉八進位制:Integer.toOctalString(i);
  • 轉十六進位制:Integer.toHexString(i);

2. 其他進位制字串轉為十進位制數值:

Integer.valueOf(String numstr, int radix); //返回Integer物件
或者
Integer.parseInt(String numstr, int radix); //返回int值

必須要標註轉為幾進位制,否則八進位制也會當做十進位制轉,即傳遞雙參,第二個引數為進位制;
另外,不要使用上節提到的記法,否則會丟擲異常NumberFormatException

三、java關於數位的運算子

  1. &
    將int值的轉化為二進位制,進行按位與

如,下面有個例子,需要計算int值的最低位是不是1,做法是
(i & 1) == 1;
1的二進位制是00000001, 因此任何數位與1相與高位都會變成0,只有最低位會保持,因此若i的最低位為1,則最後也會變成00000001, 即為1。


同理,想要擷取二進位制的哪幾位,就讓他與哪幾位為1的二進位制相與。

注意:&的優先順序沒有 比較運算子==高,因此必須加上括號。

  1. | 按位或。

或的性質是全0保持數值原型;或者設定某些位置為1讓原數值的這些位置變成1。
用的比較少。

  1. ^ 按位異或

這個符號的騷操作就多了:

  • 與0異或,可以讓原數值所有位保持原樣;
  • 與1異或,可以讓所有位翻轉
  • 交換兩個數值:

A = A ^ B; B = A ^ B; A = A ^ B;
PS:不過最好別這麼寫,否則會經常捱打。
話雖如此,因為位運算是最高效的,因此這種方法的速度是最快的。。。。

  1. ~ 按位取反
    這個是單運算子。

5.<< 左移
朝左邊就是左移,很好記
左移會在最低位補上0,不涉及最高位的正負號操作【無需考慮】。
左移一次可以將原數值 * 2
不過若是面試官問你左移一位是不是和 *2等價,必須答不是,還是那句:位運算的速度遠高於數值運算。

  1. >> 帶符號右移
    右移需要考慮到正負號。

> > i 表示將二進位制表示向右移動i位,並且按照原來數值的正負在最高位補上0或者1.

  1. >>> 無符號右移
    右移需要考慮到正負號。

> >> i 表示將二進位制表示向右移動i位,並且總是在最高位補0.
演算法題中這種用的比較多。

四、幾個例題

  1. 劍指15
請實現一個函式,輸入一個整數(以二進位制串形式),輸出該數二進位制表示中 1 的個數。例如,把 9 表示成二進位制是 1001,有 2 位是 1。因此,如果輸入 9,則該函式輸出 2。

 

示例 1:

輸入:00000000000000000000000000001011
輸出:3
解釋:輸入的二進位制串 00000000000000000000000000001011 中,共有三位為 '1'。
示例 2:

輸入:00000000000000000000000010000000
輸出:1
解釋:輸入的二進位制串 00000000000000000000000010000000 中,共有一位為 '1'。
示例 3:

輸入:11111111111111111111111111111101
輸出:31
解釋:輸入的二進位制串 11111111111111111111111111111101 中,共有 31 位為 '1'。

思路:
將i與1按位與,若為1,count++,並且i右移一位。

 public int hammingWeight(int n) {

        int count = 0;

        while (n != 0) {
            if ((n & 1) != 0) {
                count++;
            }
            n >>>= 1;
        }

        return count;
    }

可以優化的地方:
n & 1 == 1就++, 故可以直接簡寫為
count += n & 1; //裝逼如風,常伴吾身。其實是抄來的。
其實從這裡也可以看出, &的優先級別比+更高

第二種解法,既然可以讓前一位數右移,自然也能讓1左移,其他仍然是一樣的邏輯。

結束條件分析:
while(flag > 0) …

經過測試,我發現1左移31位就會變成負數,這應該是因為int型別佔據4Byte,而首位表示符號位,左移31次之後,1這個數字就到達了符號位,因此這個數就變成了負數。

而測試數字也是int型別,顯然也滿足32位,所以1左移31位就可以檢查完所有數位了,但是如果原數值最高位為1,需要出去多比較一次。

public int hammingWeight(int n) {

        int count = 0;

        int flag = 1;
        while (flag > 0) {
            if ((n & flag) != 0) {
                count++;
            }
            flag <<= 1;
        }

        if ((n & flag) != 0) {
            count++;
        }
        return count;
    }

第三種解法: 將一個數減1,就是將這個數的**二進位制最後一個1變成0,若這個數最後一個1後面還有0,就會把這些0變成1** > 如,1000 - 1 = 0111 這是,若將原數字i與i - 1相與,就會得到**i最後一位1變成0的數字**,我們稱之為i',

這個演算法就是求這樣消去最後一個1的操作能進行幾次。

   public int hammingWeight(int n) {
        int count = 0;
        while (n != 0) {
            count++;
            n &= (n - 1);
        }
        return count;
    }

最後這個解法的n & (n - 1)的操作是進位制演算法中常見的做法:
如:
(1)求一個數n是不是2的整數次方。
若一個數是2的整次方,則這個數必有且只有一位為0.
因此,直接返回return n != 0 && n & (n - 1) == 0即可。

(2)計算一個m的二進位制要改變幾位可以得到n的二進位制。

  1. 求m ^ n;
  2. 按照上面的演算法求1的個數
    因為只有兩者不一樣的才是1,改變多少位成為n就是統計m與n不一樣的數位。
 public int hasNBits(int m, int n) {
        int result = m ^ n;

        int count = 0;
        while (result != 0) {
            result &= (result - 1);
            count++;
        }

        return count;

    }