📝 个人简介
⭐ 个人主页:我是段段🙋
🍊 博客领域:编程基础、前端💻
🍅 写作风格:干货!干货!都是干货!
🍑 精选专栏:数据结构与算法
🛸 支持段段:点赞👍、收藏⭐、留言💬
前言
什么是位运算?
数据都是以二进制的形式存储在设备中,即0、1两种状态,计算机对二进制数据进行的运算(+、-、*、/)都叫位运算,即将符号位共同参与的运算
首先引入log
方法
const { log } = console
举一个简单的例子看下cpu是如何进行计算的
let a = 35
let b = 47
let c = a + b
计算两个数的和,而在计算机中都是以二进制进行计算的,所以上面定义的变量在计算机内部先转换为二进制再进行相加
35:0 0 1 0 0 0 1 1
47:0 0 1 0 1 1 1 1
-------------------
0 1 0 1 0 0 1 0
位运算概述
位运算主要包括按位与(&)
、按位或(|)
、异或(^)
、取反(~)
、左移(<<)
、右移(>>)
,具体如下
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1 |
| | 或 | 两个位都为0时,结果才为0 |
^ | 异或 | 两个位相同为0,相异为1 |
~ | 取反 | 0变为1,1变为0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0;有符号数,有的补符号位(算术右移),有的补0(逻辑右移) |
按位与运算符(&)
定义:参加运算的两个数据,按二进制位进行"与"运算
运算规则:0 & 0 = 0、0 & 1 = 0、1 & 0 = 0、1 & 1 = 1,两个数据相应位同时为1时,结果才为1,否则结果为0
例如 3 & 5
3:0000 0011
5:0000 0101
------------
0000 0001
// 即3 & 5的运算结果为1
log( 3 & 5 ) // 1
按位或运算符(|)
定义:参加运算的两个数据,按二进制位进行"或"运算
运算规则:0 | 0 = 0、0 | 1 = 1、1 | 0 = 0、1 | 1 = 1,两个数据相应位只要有一个为1,其值为1
例如 3 | 5
3:0000 0011
5:0000 0101
-------------
0000 0111
// 即3 | 5的运算结果为7
log( 3 | 5 ) // 7
异或运算符(^)
定义:参加运算的两个数据,按二进制位进行"异或"运算
运算规则:0 ^ 0 = 0、0 ^ 1 = 1、1 ^ 0 = 1、1 ^ 1 = 0,两个数据相应位相同为0,相异为1
例如 3 ^ 5
3:0000 0011
5:0000 0101
------------
0000 0110
// 即3 ^ 5的运算结果为6
log( 3 ^ 5) // 6
异或的几条性质
1. 交换律 a ^ b = b ^ a
2. 结合律 (a ^ b) ^ c = a ^ (b ^ c)
3. 对于任何数x,都有 x ^ 0 = x,x ^ x = 0
4. 自反性 a ^ b ^ b = a ^ 0 = a
log( (3 ^ 5) === (5 ^ 3) ) // true
log( ((3 ^ 5) ^ 7) === (3 ^ (5 ^ 7)) ) // true
log( 3 ^ 0, 3 ^ 3) // 3 0
log( 3 ^ 5 ^ 5 ) // 3
实例一:交换两个数值
- 借助第三个变量
let num1 = 1
let num2 = 2
function exchange(val1, val2){
let temp = val1
val1 = val2
val2 = temp
return[val1, val2]
}
[ num1, num2 ] = exchange(num1, num2)
log( num1, num2 ) // 2 1
- 加减法
let num1 = 1
let num2 = 2
function exchange(val1, val2){
val1 += val2 // val1 = 1 + 2 = 3
val2 = val1 - val2 // val2 = 3 - 2 = 1
val1 -= val2 // val1 = 3 - 1 = 2
return [val1, val2]
}
[ num1, num2 ] = exchange(num1, num2)
log( num1, num2) // 2 1
- 位运算
let num1 = 1
let num2 = 2
function exchange(val1, val2){
val1 ^= val2 // val1 = val1 ^ val2 = 1 ^ 2
val2 ^= val1 // val2 = val2 ^ val1 = 2 ^ (1 ^ 2) = 1 ^ (2 ^ 2) = 1 ^ 0 = 1
val1 ^= val2 // val1 = val1 ^ val2 = (1 ^ 2) ^ 1 = (1 ^ 1) ^ 2 = 2
return [val1, val2]
}
[ num1, num2 ] = exchange(num1, num2)
log(num1, num2) // 2 1
取反运算符(~)
定义:参加运算的一个数据,按二进制进行"取反"运算
运算规则:~1 = 0、~0 = 1,对一个二进制数按位取反,即0变1,1变0
// 对所有整数取反 = 本身的相反数 - 1
log( ~1, ~0) // -2 -1
左移运算符(<<)
定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2
let a = -4
log( a << 2) // -16
右移运算符(>>)
定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃
a = a >> 2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负
// 操作数每右移一位,相当于该数除以2
let a = 4
log( a >> 2 ) // 1
实例二:判断奇偶数
之前我们判断得时候会这样写
function isParity(val){
return val % 2 == 0 ? true : false
}
log( isParity(5) ) // false
log( isParity(4) ) // true
用位运算来实现
function isParity(val){
return val & 1 == 1 ? false : true
}
log( isParity(5) ) // flase
log( isParity(4) ) // true
实例三:位运算经典问题
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号
function sum(num1, num2){
let a = num1 ^ num2
let b = num1 & num2
b <<= 1
if(b == 0){
return a
}
return sum(a, b)
}
log( sum(5, 3) ) // 8
具体实现过程如下, 以5 + 3为例进行过程分析
第一步:num1 = 0101(5),num2 = 0011(3), a = num1 ^ num2 = 0110,b = num1 & num2 = 0001,b << = 1 = 0010
第二步:num1 = 0110,num2 = 0010, a = num1 ^ num2 = 0100,b = num1 & num2 = 0010,b << = 1 = 0100
第三步:num1 = 0100,num2 = 0100, a = num1 ^ num2 = 0000,b = num1 & num2 = 0100,b << = 1 = 1000
第四步:num1 = 0000,num2 = 1000, a = num1 ^ num2 = 1000,b = num1 & num2 = 0000,b << = 1 = 0000
此时a就是计算的结果
~~