深入理解二进制 算法必备底层知识

计算机储存方式

在这里插入图片描述

二进制

在计算机世界里,所有的信息都是以二进制的形式组成的,比如一个整型数字3,在计算机中的存在形式是0000 0000 0000 0000 0000 0000 0000 0011。一个字符’a’,在计算机中的存在形式是0110 0001(即97),你编写的一行代码#include<iostream>,在计算机中的存在形式是
(#)00100011 (i)01101001 (n)01101110 (\c)01100011 (l)01101100 (u)01110101 (d)01100100 (e)01100101 (<)00111100 (i)01101001 (o)01101111 (s)01110011 (t)01110100 (\r)01110010 (e)01100101 (a)01100001 (m)01101101 (>)00111110,当然,经过编译器的处理,所写的代码会以更简洁的形式储存

原码、反码、补码

三种储存方式均是为了负数的表示。
原码:依靠符号位判断正负数。
反码:原码基础上除符号位外其它位值取反。
补码:在反码的基础上+1。

理解:
正数在原码、反码、补码的机制上储存方式不变

如 int 型的 3:
原码:0000 0000 0000 0000 0000 0000 0000 0011
反码:0000 0000 0000 0000 0000 0000 0000 0011
补码:0000 0000 0000 0000 0000 0000 0000 0011

负数则各有各的储存法则

如 int 型的 -3
原码:1000 0000 0000 0000 0000 0000 0000 0011
反码:1111 1111 1111 1111 1111 1111 1111 1100
补码:1111 1111 1111 1111 1111 1111 1111 1101

(注:最靠左边的位是符号位,1代表负数,0代表正数)

为什么使用补码

原开始计算机储存用的是原码,有一个很明显的短板,就是在涉及运算时很难处理数据,加减时运算如下图所示在这里插入图片描述
乘除时就更麻烦了

为解决这个难题,反码就应运而生了,它并未真正解决了问题,它有正0负0的概念,在运算时也会出错,其实反码也仅仅是个概念,未被真正运用过

很快,在反码的基础上衍生了补码,即在反码的基础上+1,以1111 1111 1111 1111 1111 1111 1111 1111代表-1,它能很好地解决运算问题
在这里插入图片描述

八进制和十六进制

在平时的运用中二进制代码很少被使用,而是以八进制和十六进制代替。

八进制:以0开头,由数字0-7组成
十六进制:以0x或0X开头,由数字0-9和字母a-f或A-F组成,字母不区分大小写

以int型的 19 为例
二进制:0000 0000 0000 0000 0000 0000 0001 0011
八进制:0 000000000023
十六进制:0x 00000013
容易看到八进制以三个bit为一个单位,十六进制以四个bit为一个单位,以二进制转十六进制举例,还是19:
二进制->十六进制 19 = 0000(0) 0000(0) 0000(0) 0000(0) 0000(0) 0000(0) 0001(1) 0011(3) =0x00000013
括号里的是十六进制

有必要注意的是,不要忽略了符号位。32位的数字只有31位是有效的,0x7fffffff是最大值,而0xffffffff代表-1

可以看到,八进制和十六进制可以很好地表示二进制位,当你想求某个特定的最大范围或最小范围时,十进制显得非常吃力,而八进制和十六进制在这方面简单很多

位运算

也叫按位运算,按位操作用来操作整数基本数据类型中的单个”比特“(bit),即二进制位。按位操作符会对两个参数中对应的位执行布尔代数运算,并最终生成一个结果。
按位操作符来源于C语言面向底层的操作,在这种操作中经常需要操纵硬件,设置硬件寄存器内的二进制位。Java的设计初衷是嵌入电视机机顶盒内,所以这种面向底层的操作仍被保留了下来。但是,人们可能不会过多地用到位操作符

位操作符

位操作符有七种,包括按位操作符4种,移位操作符3种

按位操作符

  1. 按位"与"(&):如果两个输入位是1,则按位与生成一个输出位1,否则生成一个输出位0

  2. 按位"或"(|):如果两个输入位里只要有一个是1,则按位或生成一个输出位1,只有在两个输入位但是0的情况下生成一个输出位0

  3. 按位"异或"(^):如果两个输入位的某一个是1但不全是1,那么按位异或操作生成一个1

  4. 按位"非"(~):也叫取反运算符,它属于一元操作符,按位非生成与输入位相反的值----若输入0,则输出1;若输入1,则输出0。

移位操作符
5. 左移位运算符<<:能按照操作符右侧指定的位数将操作符左边的操作数向左移动(在低位补0)
6. "有符号"右移位运算符>>:能按照操作符右侧指定的位数将操作符左边的操作数向右移动。“有符号"右移操作符使用"符号扩展”:若符号为正,则在高位插入0;若符号为负,则在高位插入1。
7. "无符号"右移位运算符>>>:能按照操作符右侧指定的位数将操作符左边的操作数向右移动。“无符号"右移操作符使用"0扩展”:无论正负,都在高位插入0。这一操作符是C或C++所没有的。

理解:
以一字节的3和-6来举例;
3的二进制:0000 0011
-6的二进制:1111 1010
3&(-6)=0000 0010=2;
3|(-6)=1111 1011=-5;
3^(-6)=1111 1001=-7;
~3=1111 1100=-4;
~(-6)=0000 0101=5;
3<<1=0000 0110=6;
3>>1=0000 0001=1;
3>>>1=0000 0001=1;
(-6)<<1=1111 0100=-12;
(-6)>>1=1111 1101=-3;
(-6)>>>1=0111 1101=125;

补码运算

整数运算
加法(+):按位相加
减法(-):按位相减
乘法(*):分为2次幂相乘和常数相乘
除法(/):分为2次幂相除和常数相除

理解:
以一字节的3和-6来举例;
3的二进制:0000 0011
-6的二进制:1111 1010

加减法
3+(-6)=1111 1101=-3;
3-(-6)=0000 1001=9;

乘法
要计算3*(-6)首先要理解值的算法
对于一字节整数3
3=1*20+1*21+0*22+0*23+0*24+0*25+0*26
容易得到

3*20=(1*20+1*21+0*22+0*23+0*24+0*25+0*26)*20=3<<0=0000 0011=3
3*21=(1*20+1*21+0*22+0*23+0*24+0*25+0*26)*21=3<<1=0000 0110=6

当求3*11时
11=0000 1011=1*20+1*21+1*23
则3*11=3*(1*20+1*21+1*23)=3<<0+3<<1+3<<3=33

对于负数-6
-6=-1*27+(0*20+1*21+0*22+1*23+1*24+1*25+1*26)
同样利用分配律得
3*(-6)=3*(-1*27+1*21+1*23+1*24+1*25+1*26)=(-(3<<7)+(3<<1)+(3<<3)+(3<<4)+(3<<5)+(3<<6))=-18

除法
对于一字节整数3
3=1*20+1*21+0*22+0*23+0*24+0*25+0*26
容易得到

3/20=(1*20+1*21+0*22+0*23+0*24+0*25+0*26)/20=3>>0=3
3/21=(1*20+1*21+0*22+0*23+0*24+0*25+0*26)/21=3>>1=1 …

对于常数除法困难度有点大,大家有兴趣自己去了解吧

位运算的应用

在这里插入图片描述

1.交换两个数的值
通过三个运算交换x、y的值:
x=x^y;
y=x^y;
x=x^y;
实质上是
y=x^y^y=x;
x=x^y^x=y;
这仅仅是个二进制智力游戏,并不会带来性能的提升,可以以此深入学习二进制的一些运算

2.求一个数二进制有多少个1
方法1:
遍历所有位数

for(int i = 0;i < 32;i++)
{
	if((m>>i)&1==1)//m为待求数 
	count++;//1的数量 
}

方法2:
利用m&=(m-1)

while(m)
{
	count++;
	m&=(m-1);
}

解释:
m&(m-1)可以消除最后一个1,以6为例
6=0000 0101;
6-1=0000 0100;
6&(6-1)=0000 0100=5;
5-1=0000 0011;
5&(5-1)=0000 0000;
可以看出m&(m-1)可以消除最后一个1;

3.二进制是一个很好的散列表
很多算法结构都基于二进制实现的,如大小堆(优先级队列),树状数组等,它也可以代表组合例如a[2]={1,2,3},它的子数组有
0=000={};
1=001={3};
2=010={2};
3=011={2,3};
4=100={1};
5=101={1,3};
6=110={1,2};
7=111={1,2,3};
二进制有很多用法,深入学习二进制对算法学习有很大帮助,大家加油吧

4.非常妙的二进制实战,一定要学习一下
老鼠试药

在这里插入图片描述

创作不易点赞三连!!!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旧林墨烟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值