位运算的基本知识
进制
进制的概念
任何一种进位制都有一个基数,基数为X的进位计数称为X进制,表示每一个数位上的数运算都是逢X进一。
对于一个X进制的数,其具体数值由其中的每个数码和数码所在的数位决定。整数部分从右往左的第m个数位表示的权重是$X^m$,其中m最小值为0;小数部分从左往右的第n个数位表示的权重是$X^{-n}$,其中n最小为1。
十进制的123.45可以写成如下形式:$123.45=1\times10^2+2\times10^1+3\times10^0+4\times10^{-1}+5\times10^{-2}$
八进制的720.5可以写成如下形式:$720.5_{(8)}=7\times8^2+2\times8^1+0\times8^0+5\times8^{-1}$
常见的进制
二进制、八进制、十进制、十六进制
进制间的转换
非十进制转十进制
将非十进制数转成十进制数,只要将每个数位的加权和即可。
例如:八进制的720.5可以写成如下形式:$720.5_{(8)}=7\times8^2+2\times8^1+0\times8^0+5\times8^{-1}=464.625$
十进制转非十进制
将十进制转成X进制,需要对整数部分和小数部分分别转换。
对于整数部分,转换方式为将十进制的整数部分每次除以X直到变成0,并记录每次的余数,反向遍历每次的余数即可得到X进制表示。
例如:将十进制数50转成二进制:
$50/2=25…0;25/2=12…1;12/2=6…0;6/2=3…0;3/2=1/…1;1/2=0…1$
反向遍历得110010
对于小数部分,转换方式是将十进制的小数部分乘以X直到变成0,并记录每次的整数部分,正序遍历每次的整数部分即可得到X进制表示。
例如:将十进制数0.6875转成二进制
$0.6875\times2=1.375整1;0.375\times2=0.75整0;0.75\times2=1.5整1;0.5\times2=1整1$
正向遍历得到1011。
需要注意的是,在一种进制下的有限小数,转成另一种进制之后可能变成无限循环小数。例如十进制数0.2转成二进制数是0.0011。
其他进制间的转换
如果需要在两个不同的非十进制之间转换,常规思路是先转成十进制,再转成目标进制数。在一些特殊情况下,也可以不经过十进制,直接进行转换。
一位八进制可以表示成三位二进制,一位十六进制可以表示成四位二进制。
整数在计算机中的表示方式
计算机中的二进制
计算机采用的是二进制,二进制包括两个数码:0,1
一位二进制数可能取值有2个,k位二进制数的可能取值就有$2^k$个
有符号整数和无符号整数
计算机中的数据类型包括有符号类型和无符号类型,有符号类型的整数称为有符号整数,无符号类型的整数称为无符号整数。
- 有符号整数的取值范围包括负整数、零、正整数,无符号的整数取值范围包括零和正整数,不包括负整数
- 在位数相同的情况下,有符号整数可以表示的最大值比无符号整数小了一半
- 在对于k位整数,有符号整数的取值范围是$-2^{k-1}到2^{k-1}-1$,无符号整数的取值范围是$0到2^{k}-1$
原码、补码和反码
机器数和真数
一个数在计算机中的二进制表示形式称为这个数的机器数。机器数是有符号数,机器数的最高位是符号位,0表示0或者整数,1表示负数。
以八位二进制数为例。十进制数+10转换成二进制数是00001010,十进制数-10转换成二进制数是10001010。这里二进制数为机器数。
例如10001010的形式值是138,真正数值是-10,形式值和真正数值是不同的。为了加以区分,将机器数的真正数值称为机器数的真值。
原码
原码是机器数的符号位加上机器数的真值的绝对值,最高位是符号位,其余表示数值。
以八位二进制为例:+10的原码是00001010,-10的原码是10001010。
八位二进制的原码表示的最大值是01111111,即+127,最小值是11111111,即-127,因此八位二进制的原码表示的取值范围为-127到127。
反码
反码是在原码的基础上得到的。
0和正数的反码与原码相同,负数的反码为将原码的除了符号位之外的每一位取反。
以八位二进制为例:
- +10的原码是00001010;反码是00001010
- -10的原码是10001010;反码是11110101
补码
补码是在反码的基础上得到的。
0和正整数的补码与原码、反码相同,负数的补码是在反码的基础上+1得到的。
以八位二进制为例:
- +10的原码是00001010;反码是00001010;补码是00001010
- -10的原码是10001010;反码是11110101;补码是11110110
计算机中的表示
原码存在的问题:
- 同时存在+0和-0的表示
- 用原码进行减法运算,会导致错误的结果
反码的引入,解决了源码的减法运算结果错误的问题,但是仍然没有解决存在+0和-0的问题。
补码的引入同时解决了减法运算错误和同时存在+0和-0的问题,而且可以多表示一个最小值。10000000表示-128
位运算的概念和性质
位运算概述
与、或、异或、取反、左移、右移
与、或、异或、取反
移位运算
移位运算按照移位方向可以分成左移或右移,按照是否带符号分类可以分成算术位移和逻辑位移。
左移运算的符号是<<。左移运算时,将全部二进制位向左移动若干位,高位丢弃,低位补0。对于左移运算,算数位移和逻辑位移是相同的。
右移运算的符号是>>。右移运算时,将全部二进制向右移动若干位,低位丢弃,高位的补位由算术位移或逻辑位移决定:
- 算术右移,高位补最高位
- 逻辑右移,高位补0
对于Java而言,不存在无符号类型,所有的表示整数的类型都是有符号类型,因此需要区分算术右移和逻辑右移。在Java中,算术右移的符号是>>,逻辑右移的符号是>>>
移位运算与乘除法的关系
左移运算对应乘法。将一个数左移k位,等价于将这个数乘以$2^k$。当乘数不是2的整数次幂时,可以将乘数拆成若干项2的整数次幂之和,例如$a\times6等价于(a<<2)+a(<<1)$。对于任意整数,乘法运算都可以用左移运算实现,但是需要注意溢出的情况。
算术右移运算对应除法运算。将一个数右移k位,相当于将这个数除以$2^k$。对于0和正数,将一个数右移K位,和这个数除以$2^k$等价。但对于负数来说是不等价的。
位运算性质
- 幂等律:a&a=a,a|a=a
- 交换律:a&b=b&a,a|b=b|a,$a\bigoplus b=b\bigoplus a$
- 结合律:(a&b)&c = a&(b&c),(a|b)|c= a|(b|c),$(a\bigoplus b)\bigoplus c=a\bigoplus(b\bigoplus c)$
- 分配律:(a&b)|c = (a|c)&(b|c),(a|b)&c=(a&c)|(b&c),$(a\bigoplus b) \& c=(a\&c)\bigoplus(b\&c)$
- 德·摩根律:~(a&b) = (~a)|(~b),~(a|b)=(~a)&(~b)
- 取反运算性质:-1=~0,-a=~(a-1)
- 与运算性质:a&0=0,a&(-1)=a,a&(~a)=0
- 或运算性质:a|0=a,a|(~a)=-1
- 异或运算性质:$a\bigoplus0=a$,$a\bigoplus a=0$
- 其他性质:
- a&(a-1)结果为将a的二进制表示的最后一个1变成0;
- a&(-a)的结果为只保留a的二进制的最后一个1,其余的1都变成0。
七进制数
递归
1 | class Solution { |
迭代
1 | class Solution { |
数字转换为十六进制数
1 | class Solution { |
位1的个数
1 | public class Solution { |
颠倒二进制位
1 | public class Solution { |
两整数之和
1 | class Solution { |
格雷码
1 | class Solution { |
数字范围按位与
1 | class Solution { |
比特位计数
1 | class Solution { |
只出现一次的数字
1 | class Solution { |
只出现一次的数字II
1 | class Solution { |
只出现一次的数组III
1 | class Solution { |
2的幂
1 | class Solution { |
3的幂
1 | class Solution { |
4的幂
1 | class Solution { |
Pow(x,n)
1 | class Solution { |
飞机座位分配概率
1 | class Solution { |
矩形重叠
1 | class Solution { |
矩形面积
1 | class Solution { |
缀点成线
1 | class Solution { |
有效的正方形
1 | class Solution { |
计算质数
1 | class Solution { |
检查【好数组】
1 | class Solution { |
不同路径
1 | class Solution { |
杨辉三角
1 | class Solution { |
杨辉三角II
1 | class Solution { |
巴什博弈
1 | class Solution { |
除数博弈
1 | class Solution { |
石子游戏
1 | class Solution { |
你可以获得的最大硬币数目
1 | class Solution { |