位运算,能不能一次记住!
写给新手朋友的位运算,你是不是也是总是不记得,记了又忘记,记住了又不知道怎么运用到编程中?那记一次理论+题目的方式理解记忆,搞清楚它吧!
我们接触最多的是十进制数,加减乘除这种四则远算其实就是二进制数据中的一种运算法则,当谈到位运算,它不是用来操作十进制数的,我们实际上是在操作二进制数的不同位。位运算在前端开发中可能不常用,但了解它们对你理解计算机底层运作和一些特定情况下的优化是有帮助的。
接下来我们从几种常见的位运算开始,以及它们的使用场景,好好理解一番。
1. 二进制转换
既然是写给新手朋友也能看得明白的,那就顺带提一下二进制数吧(熟悉二进制的可以跳过这段)
当计算机处理数据时,它实际上是在执行一系列的二进制操作,因为计算机内部使用的是电子开关,这些开关只能表示两个状态:开(表示1)和关(表示0)。因此,计算机中的所有数据最终都被转换为二进制表示。
二进制(binary)是一种使用两个不同符号(通常是 0 和 1)来表示数字、字符、图像等信息的数字系统。这种二元系统是现代计算机科学的基础。
十进制到二进制的转换:
将十进制数转换为二进制数的过程涉及到不断地除以2,然后记录余数。最后,将这些余数按相反的顺序排列,就得到了对应的二进制数。
例如,将十进制数 13 转换为二进制数:
- 13 除以 2 得商 6,余数 1
- 6 除以 2 得商 3,余数 0
- 3 除以 2 得商 1,余数 1
- 1 除以 2 得商 0,余数 1
将这些余数按相反的顺序排列,得到二进制数 1101。
或者你也可以这么想
- (1 || 0) * 2^n + (1 || 0) * 2^(n-1) + ... + (1 || 0) * 2^0 = 13
- 只需要满足以上公式,加出来你想要的值
- 2 的 4次方大于13,2的3次方小于13,那么就从2的3次方开始依次递减到0次方
- 1 * 2^3 + 1 * 2^2 + 1 * 2^1 + 1 * 2^0 显然 8 + 4 + 2 + 1 = 15已经超出了13,所以你得在这个式子中减少2
- 1 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0 取该等式中的1,0;所以 13 的二进制是 1101
以上两种方式都能得出一个数的二进制,看你喜欢
二进制到十进制的转换:
将二进制数转换为十进制数的过程涉及到将每个位上的数字与2的幂相乘,然后将这些结果相加。
例如,将二进制数 1101 转换为十进制数:
- 第0位(最右边)上的数字是 1,表示 2^0 = 1
- 第1位上的数字是 0,表示 2^1 = 0
- 第2位上的数字是 1,表示 2^2 = 4
- 第3位上的数字是 1,表示 2^3 = 8
将这些结果相加:1 + 0 + 4 + 8 = 13,得到十进制数 13。
在编程中,通常会使用不同的函数或方法来实现十进制到二进制以及二进制到十进制的转换,这些转换可以帮助我们在计算机中处理和表示不同的数据。
2. 按位与(&)
按位与运算会将两个数字的二进制表示的每一位进行 与 操作,如果两个相应位都是 1,则结果为 1,否则为 0。
使用场景: 常用于权限控制和掩码操作。
一道题让你更好的理解它的用法
题目:判断一个整数是否是2的幂次方。
问题描述:给定一个整数 n
,判断它是否是2的幂次方,即是否满足 n = 2^k
,其中 k
是非负整数。
使用位运算中的按位与操作可以很巧妙地解决这个问题。
思路:如果一个数 n
是2的幂次方,那么它的二进制表示一定只有一位是1,其他位都是0(例如:8的二进制是 1000)。而 n - 1
的二进制表示则是除了最高位的1之外,其他位都是1(例如:7的二进制是 0111)。如果我们对 n
和 n - 1
进行按位与操作,结果应该是0。
那我们可以这么写:
在这个示例中,我们巧妙的使用了 (n & (n - 1))
来检查是否满足条件,如果结果为0,说明 n
是2的幂次方。
希望这个示例能够帮助你更好地理解按位与运算的应用方式!
2. 按位或(|)
按位或运算会将两个数字的二进制表示的每一位进行或操作,如果两个相应位至少有一个是 1,则结果为 1,否则为 0。
使用场景: 常用于设置选项和权限。
一道题让你更好的理解它的用法
题目:如何将一个整数的特定位设置为1,而不影响其余位。
问题描述:给定一个整数 num
,以及一个表示要设置为1的位的位置 bitPosition
(从右向左,最低位的位置为0),编写一个函数将 num
的第 bitPosition
位设置为1。
我们可以使用按位或运算来实现这个效果
在这个示例中,我们首先创建了一个掩码 mask
(这里用到了另一个位运算,左移,下面会讲到),它只有第 bitPosition
位是1,其他位都是0。然后,我们使用按位或运算 num | mask
将 num
的第 bitPosition
位设置为1,得到了结果。
这个问题演示了如何使用按位或运算来修改一个整数的特定位,而不影响其他位。希望这个示例能帮助你更好地理解按位或运算的应用方式!
3. 按位异或(^)
按位异或运算会将两个数字的二进制表示的每一位进行异或操作,如果两个相应位不相同则结果为 1,相同则为 0。
使用场景: 常用于数据加密和校验。
一道题让你更好的理解它的用法
题目:如何交换两个整数的值,而不使用额外的变量
问题描述:给定两个整数 a
和 b
,编写一个函数来交换它们的值,而不使用额外的变量。
我们可以使用按位异或运算来实现这个效果:
上述代码中,我们首先将 a
更新为 a ^ b
,这使得 a
包含了 a
和 b
的异或值。然后,我们使用同样的方法将 b
更新为 a
的原始值,最后,我们再次使用异或运算将 a
更新为 b
的原始值,完成了交换操作。
此处应该沉思,思考清楚这个问题:(
a ^ b
) ^ b 得到的是 a 的原始值
不使用额外的变量来做两个变量值的交换,这还是个面试题哦!
4. 按位非(~)
按位非运算会将一个数字的二进制表示的每一位取反,即 0 变成 1,1 变成 0。它将操作数转化为 32 位的有符号整型。
一道题让你更好的理解它的用法
题目:反转二进制数的位,然后返回其对应的十进制数
问题描述:给定一个二进制字符串,编写一个函数来反转该字符串的位,并返回其对应的十进制数。
这里你可能会有疑问,为什么13的二进制取反会的到-14,这里就不得不介绍一下 补码 的概念了
5. 补码小插曲
假设我们要求 -6 的二进制,那就相当于是求 -6 的补码
因为负数的二进制表示通常使用二进制补码来表示。要计算-6的二进制补码表示,可以按照以下步骤操作:
- 首先,找到6的二进制表示。6的二进制表示是
00000110
。 - 然后,对6的二进制表示进行按位取反操作,即将0变成1,将1变成0。这将得到
11111001
。 - 最后,将取反后的结果加1。
11111001
+ 1 =11111010
。
所以,-6的二进制补码表示是 11111010
。在补码中,最高位表示符号位,0表示正数,1表示负数,其余位表示数值的绝对值。因此,11111010
表示的是-6。
注意:
-6的二进制补码表示的位数不一定是8位。位数取决于数据类型和计算机系统的规定。在许多计算机系统中,整数的表示采用固定的位数,通常是32位或64位,但也可以是其他位数,例如16位。
在常见的32位表示中,-6的二进制补码表示可能是 11111111111111111111111111111010
。这是32位二进制,其中最高位是符号位(1表示负数),其余31位表示数值的绝对值。
在64位表示中,-6的二进制补码表示可能是 1111111111111111111111111111111111111111111111111111111111110
。这是64位二进制,同样,最高位是符号位,其余63位表示数值的绝对值。
因此,-6的二进制补码表示的位数取决于计算机系统和数据类型的规定。不同的系统和数据类型可能采用不同的位数。
6. 左移(<<)和右移(>>)
左移运算将一个数字的二进制表示向左移动指定的位数,右移运算将二进制表示向右移动指定的位数。
注意:因为我们的计算可以是32位或者是64位的,所以理论上 5 的二进制应该是 00... 00000101, 整体长度为32或者64。 左移我们只是把有效值 101 向左拖动,右边补0,右移左边补 0, 但是要保证整体32或64位长度不能变,所以,右移会砍掉超出去的值
一道题让你更好的理解它的用法
题目: 如何实现整数的乘法和除法,使用左移和右移操作来提高效率。
问题描述:编写一个函数,实现整数的乘法和除法运算,但是只能使用左移和右移操作,不能使用乘法运算符 *
和除法运算符 /
。
这也是一道面试题,实现起来很简单
想清楚,一个数的二进制,每次左移一位的结果会怎么样?
比如 6 的二进制是 00000110, 左移一次后变成 00001100,
也就是说 从 2^2 + 2^1 变成了 2^3+ 2^2 。 4 + 2 变成了 8 + 4。
所以每左移一位,都相当于是原数值本身放大了一倍
这样你是否更清楚了用左移来实现乘法的效果了呢?
最后
以上列举的是常见的位运算方法,还有一些不常见的,比如:
- 位清零(Bit Clearing):将特定位设置为0,通常使用按位与运算和适当的掩码来实现。
- 位设置(Bit Setting):将特定位设置为1,通常使用按位或运算和适当的掩码来实现。
- 位翻转(Bit Flipping):将特定位取反,通常使用按位异或运算和适当的掩码来实现。
- 检查特定位:通过使用按位与运算和适当的掩码来检查特定位是否为1或0。
- 位计数:计算一个整数二进制表示中1的个数,这通常使用一种称为Brian Kernighan算法的技巧来实现。
- 位交换:交换两个整数的特定位,通常使用按位异或运算来实现。
等等...有兴趣的可以自行摸索了
来源:juejin.cn/post/7274188187675902004