二进制究竟有什么用?齐姐带你看看那些好玩儿的「位操做」

本篇终于讲到了齐姐文章里经常出现的分割线!面试

计算机说到底就是 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 月你想学习的天数和天天学习的时长,咱们一块儿学习抱富!~

相关文章
相关标签/搜索