位运算以及实际应用
位运算
计算机中的数在内存中都是以二进制形式进行存储的 ,而位运算就是直接对整数在内存中的二进制位进行操作,因此其执行效率非常高,在程序中尽量使用位运算进行操作,这会大大提高程序的性能。
| 符号 | 描述 | 运算规则 | 实例(以四位二进制数为例) |
|---|---|---|---|
| & | 与 | 两个位都为1时,结果才为1。 | 0001&0001=1,0001&0000=0,0000&0000=0000 |
| | | 或 | 两个位都为0时,结果才为0。 | 0001|0001=0001,0001 |
| ^ | 异或 | 两个位相同为0,相异为1。 | 0001∧0001=0000,0001∧0000=1,0000∧0000=0 |
| ~ | 取反 | 0变1,1变0。 | ∼0=1,∼1=0 |
| << | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0。 | 0001<<k=0100,k=2,k kk是左移的位数,这里k = 2 k=2k=2 |
| >> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,右移补1 11。 | 0100>>k=0001,k=2,k kk是右移的位数,这里k = 2 k=2k=2 |
异或可以看作不进位相加
如1^7 即001^111,将每一位相加,但是1+1不进位.
001
111
110
此时结果就为110,即6
异或满足交换律和结合律
a^b = b^a
a(bc) = (ab)c
可以通过上面的不进位相加来理解,因为不进位,所以可以直接将每一个数的每一位相加,不管顺序如何变,结果是不会变的。
** 异或运算的性质 **
a ^ 0 = a
a ^ a = 0
可以将一个二进制数代入理解
左移
二进制的左移相当于十进制的乘2,右移同理
实际应用
交换数据
a = x , b = y,交换a,b的值
- 正常方法
tmp = a;
a = b;
b = tmp;
需要一个中间变量tmp来存储其中一个数的值,才能交换。
- 使用异或
a = a ^ b; // a = x ^ y
b = b ^ a; // b = y ^ x = y ^ x ^ y = x
a = a ^ b; // a = a ^ b = x ^ y ^ x = y
根据异或的性质可以实现,不需要另外的数来实现交换,优化了空间复杂度,且位运算相比算术运算要快。
找到二进制数最右边的 1
如1000100,即找到从左往右第五位的 1
a = 1000100;
b = a & ( ~a + 1 )
// a = 1000100
// ~a = 0111011 按位取反
// ~a + 1 = 0111100
//a & ( ~a + 1) = 0000100 与: 都为 1 时才结果为 1
实例: 给出一个二进制数N,求出该数有几个 1
int count = 0 ;
while ( N != 0){
int rightone = N & ( ~N +1 ); //得到最右边的 1
count++;
N ^= rightOne; //相与 除去最右边的1
} //循环直到N == 0
数组中有两个数出现了奇数次 , 其他数都为偶数次 , 求这两个数
设数组arr中有a,b两个数为奇数次,其余为偶数次,求a,b
int eor = 0;
for(int i = 0; i < arr.length ; i++ ){
eor ^= arr[i];
}
//eor与整个数组异或,因为两个相同的数异或为0,所以偶数次的数可直接消掉,奇数次的数也只会剩下a, b
//所以 eor = a ^ b , 且不等于 0
//所以 a != b ,eor 上必然有一位数为 1
//通过这一位可将数组分为两个部分 , 这位为0,或者这位为 1 ,而a,b也会被分别分到这两部分
//通过上面例子的方法可找到这一位数rightOne
int rightOne = eor & ( ~eor + 1 );
//rightOne 为 eor最右边的 1
int onlyOne = 0;
for(int i = 0; i < arr.length; i++ ){
if( arr[i] & rigthOne != 0){ // 通过rightOne将数组分为两部分, 将这部分的所有数异或,偶数次的数依旧不会影响,最后只剩下a,b其中一个数,这里用onlyOne表示
onlyOne ^= arr[i];
}
//另一个数着用eor ^ onlyOne 即可求出
}
浙公网安备 33010602011771号