二進制究竟有什麼用?齊姐帶你看看那些好玩兒的「位操做」

本篇終於講到了齊姐文章裏經常出現的分割線!面試

計算機說到底就是 0 和 1,全部的數在內存中都是以二進制的形式儲存的。算法

而位操做,或者說位運算,就是直接對內存中的二進制位進行操做。瀏覽器

位運算能夠說是咱們的基本功,今天這篇文章就從如下角度和你們一塊兒玩轉位運算。網絡

  1. 二進制究竟有什麼用?
  2. 原碼 反碼 補碼
  3. 7 種位運算

固然了,位運算還有不少奇技淫巧,若是你們還想看進階篇,記得給我點贊或者留言告訴我哦~工具

二進制的做用

在實際生產中,二進制是用來優化時間和空間的。學習

二進制的運算,可能並不會下降複雜度的等級,可是能夠把複雜度前面的係數降下來。優化

舉個例子。網站

你們都知道堆,或者叫優先隊列,通常來講是用徹底二叉樹來實現的,叫作二叉堆spa

最小堆

最小堆操作系統

二叉堆插入、刪除元素的時間複雜度都是 O(logn),若是這個不清楚的同窗趕忙在公衆號內回覆「」複習一下,或者點擊這裏

可是有另外一種堆,它可以作到 O(1) 的時間插入元素,O(logn) 的時間刪除元素,我在堆這篇文章裏也提到過,就是斐波那契堆

但爲何不用呢?

就是由於 O(1) 前面的係數很是大。

咱們說 O(logn)O(1) 好,是有個條件的,那就是 n 很是很是大的狀況下,可是實際上,若是 n 是在 int 範圍內,那麼取個 log 也不過就是 32 了,反而這個 O(1) 的時間複雜度可能係數達到幾百幾千。

通常來講實際應用中時間的測量並非時間複雜度這麼簡單,有的時候就須要你把兩個算法都實現出來,去跑去測量它的時間,才能決定哪一個好。

那麼二進制一次可以做用於 32 位上(假設是一個 int),若是數據表示的巧妙,這徹底能夠優化 32 倍,多用幾個 int 就多優化了好幾個 32 倍,不香嗎?

除了優化時間,還能夠優化空間。

好比在網站發佈新版本時,通常都會附上支持該版本的瀏覽器列表,否則有些老掉牙的瀏覽器看不到個人新功能還算個人鍋麼?

那麼怎麼有效的表示這個瀏覽器列表呢?

全世界全部瀏覽器都有個國際標準編號,這裏我就簡單假設一下:

  • 0 表示 QQ 瀏覽器
  • 1 表示 Chrome 瀏覽器
  • 2 表示火狐瀏覽器
  • 3 表示 ...

那麼咱們就能夠用一個 int 表示是否支持 32 個瀏覽器的狀態,若是這個瀏覽器能用,那麼這一位上就設爲 1,那麼好比國內的某個網站能夠表示爲:

  • 0b .... 1101

因此位操做在不少代碼裏都很經常使用,好比網絡協議、操做系統等等。

接下來咱們說說具體的知識點。

原碼 反碼 補碼

數字有正有負,Java 中用的是 signed type,就是有正有負的。

雖然在 Java 8 以後,也用了個工具來實現 unsigned type,可是其實底層實現是沒有的。

二進制最左邊的一位是符號位,

  • 0 表示這個數是非負數;
  • 1 表示這個數是負數。

對了,最左邊的一位英文叫作 most significant bit,好多同窗面試說的五花八門。。。

正數

正數的原碼反碼補碼相同,沒啥好說的。

好比:

  • int 1 = 0b 0000 0000 0000 0001
  • int 2 = 0b 0000 0000 0000 0010

負數:

原碼:把相應的正數的符號位設爲 1。

  • -1 的原碼 = 0b 1000 0000 0000 0001
  • -2 的原碼 = 0b 1000 0000 0000 0010

反碼 ones' complement:符號位是 1,其他位取反。

  • -1 的反碼 = 0b 1111 1111 1111 1110
  • -2 的反碼 = 0b 1111 1111 1111 1101

補碼 two's complement :反碼 + 1。

  • -1 的補碼 = 0b 1111 1111 1111 1111
  • -2 的補碼 = 0b 1111 1111 1111 1110

而計算機中真正用來存儲數據的是用補碼

這裏稍微注意下反碼和補碼的英文,ones' 的這個 ' 在後面,two's 的這個 ' 在中間。。

爲何計算機要用補碼來存儲數據呢?

可能有同窗會說正零負零的緣由,但這只是表面現象。

實際上經過補碼這樣精巧的設計,計算機作加減乘除運算就不用考慮符號,就可讓硬件裏 CPU 的設計變得異常簡單。

最初計算機只有加法器沒有減法器,因此它用這麼一種方式用加法完成了減法。

int 的最大值是多少?

正是由於最左邊一位是符號位,因此正數的表示就少了一位能用的,那麼 int 的最大值就是:

0111111...11 (31 ones) = 2^31 - 1 = 2147483647

7 種位運算

運算符

中文

英文

運算規則

<<

左移

left shift

右邊補充 0

>>

右移

signed right shift

左邊補充符號位

>>>

無符號右移

unsighed right shift

Java 特有,左邊補充 0

~

位非

NOT

每位取反

&

位與

bitwise AND

每位作與操做,都是 1 則爲 1,不然爲 0

I

位或

OR

每一位作或操做,有 1 則爲 1,不然爲 0

^

異或

XOR

相同爲 0,不一樣爲 1

要注意的是前 4 個運算符是對 1 個數進行操做的,且操做完成後這個數自己的值不變;後 3 個操做是兩個數的運算。

咱們一一來看。

爲了書寫方便,下面的數值雖然是 int 類型,但我只寫 8 位,你們都能理解的噢!

1. <<

左移操做就是把這些零啊壹啊的總體往左移動 n 位,右邊缺的就補充 0。

  • 1 = 0b 0000 0001
  • 1 << 1 = 0b 0000 0010 = 2
  • 2 = 0b 0000 0010
  • 2 << 1 = 0b 0000 0100 = 4
  • 3 = 0b 0000 0011
  • 3 << 1 = 0b 0000 0110 = 6

誒,你們發現沒有,左移 1 位以後這個數至關於乘 2

可是這隻適用於左邊溢出的高位中不包含 1 時。

若是把 1 扔了,那就確定不是 2 倍了嘛。

2. >>

  • 1 = 0b 0000 0001
  • 1 >> 1 = 0b 0000 0000 = 0
  • 2 = 0b 0000 0010
  • 2 >> 1 = 0b 0000 0001 = 1
  • 3 = 0b 0000 0011
  • 3 >> 1 = 0b 0000 0001 = 1

同理,右移操做的效果是這個數除以 2

若是是負數呢?

  • -3 = 1111 1101
  • -3 >> 1 = 1111 1110 = -2

由於 Java 是向零取整,因此奇數時會有問題,就再也不是除以 2 的結果。

總結一下,

  • 對於非負數、負數且是偶數,右移一位與除以 2 結果同樣;
  • 對於負數且是奇數,右移一位不等於除以 2。

3. >>>

和 >> 的不一樣之處在於,這個的左邊不論正負,一概補充 0。

因此對於正數來講,和 >> 的效果同樣,可是負數不一樣。

  • -3 = 1111 1101
  • -3 >> 1 = 01111 1110 = 很大的數。。

4. ~

取反操做,就是每一位取反,1 變成 0,0 變成 1。

  • 3 = 0b 0000 0011
  • ~3 = 0b 1111 1100

5. &

這個符號其實和邏輯與運算 && 意思同樣,只不過做用在每一位上。

對於每一位來講,兩個數都是真,則爲真,不然爲假。

  • 3 = 0b 0000 0011
  • 5 = 0b 0000 0101
  • 3&5 = 0b 0000 0001

6. |

同理,和邏輯或運算 || 意思同樣,只不過做用在每一位上。

對於每一位來講,但凡是有個真的就是真,不然爲假。

  • 3 = 0b 0000 0011
  • 5 = 0b 0000 0101
  • 3|5 = 0b 0000 0111

7. ^

最後一個異或操做,相同爲 0,不一樣爲 1。

  • 3 = 0b 0000 0011
  • 5 = 0b 0000 0101
  • 3^5 = 0b 0000 0110

對了,本週末新建了國內讀者交流羣,想加入的小夥伴後臺回覆「進羣」拉你進羣呀~

另外 8 月自習室活動最後一週了,給咱們自習室的小夥伴打起,應該有很多小夥伴能拿到齊姐的紅包了,還沒學夠 21 天的要繼續加油呀!

9 月的自習室正在籌備中,若是你想參加,告訴我 9 月你想學習的天數和天天學習的時長,咱們一塊兒學習抱富!~

相關文章
相關標籤/搜索