每每看见人家用几行不知是啥的代码就完成了题目,总是有着羡慕的眼观,今天将给大家总结下这些位运算操作,既然是总结这些技巧,一定就不会太难,一步一步的理解,分分钟干掉它

1 为什么使用位运算

计算机做运算的时候其实为01的运算,即符号位共同参加的运算,不信我们看看。

  • 位运算是一种底层运算,通常来说比普通的运算会快很多,至于为啥,下面会慢慢道来
  • 位运算使得程序更加简洁,但是如果没太掌握,就会感觉可读性比较差
  • 位运算操作的是二进制数,拥有一些二进制的特性

位运算有啥优势?

位运算的优势在于贴近硬件底层设计,提高程序运行效率。例如,整数乘法指令往往是相当慢的,需要10个甚至更多时钟周期,除法更慢,需要30个或更多的时钟周期,而加减法、位级运算和移位操作只需要1个时钟周期。在平时我们的计算中可能不太在意这些效率差异,但是如果涉及到超大型的计算,哪怕1%的效率提升都有重大意义,更何况这里十倍数十倍的差异!

基本概念

定义

按照位与的方式将两个操作数对应的每一个位进行逻辑与操作,满足1&1=1,1&0=0,0&0=0。举个例子

位或(|)

两个操作数对应的每一位进行逻辑或操作,满足1|0=1,0|1=1,1|1=1,0|0=0

位运算总结_位取反异或(^)

按位异或是将两个操作数对应的每一位进行逻辑异或操作,满足11=0,00=0,10=1,01=1,

位运算总结_操作数_02按位取反(~)

按位取反是将单个操作数对应的每一位取反,1=0,0=1,例如

位运算总结_位运算_03左移右移

左移是按照指定的位数将一个数向左移位。左移后,低位补0;高位舍弃,例如

位运算总结_操作数_04

右移是按照指定的位数将一个数向右移位。右移后,低位舍弃;若为无符号数,则高位补0,若为有符号数,则高位补符号位或补0(取决于逻辑右移还是算数右移),例如算术右移

位运算总结_位运算_05

2 位运算的装逼之路

为什么要讲位运算,同样的面试官不同的面试人,同样的题目,人家三十行代码写完,效率一般。你却几行代码完事儿,就是这么豪横。

  • 判断数是奇数还是偶数

常规操作:除以2,判断余数是否为0,如果使用python则如下

def IsOdd(x):
return True if (x%2<>0) else False

如果使用位计算怎么操作?

这里使用&运算,与1进行&,如果为1则为奇数;如果为0则为偶数

def IsOdd(x):
return True if (x & 1) else False
  • 交换两个数值

常规操作:引入第三个temp进行交换即可,但是这样的操作在面试的过程当然是不过关的,那么使用位计算又是怎么操作

a ^= b;
b ^= a;
a ^= b;

看似不怎么好理解,这里使用了异或,异或的意思是任意数和自身异或结果为0;0和任意的数异或还是本身。我们拆分一下

a = a ^ b
b= b a = b a b,由于bb=0,所以b=a^0,即b=a

a = a b ,由于a在第一步重新赋值,所以,a = a b ^ a = b,完成了数值交换。

  • 数字判重

二进制中非常重要的性质:abb=a。可知当同一个数累计进行两次xor操作的时候相当于自行低效了,剩下的就是不重复的数。

  • 找到没有重复的数
int find(int[] arr){
int tmp = arr[0];
for(int i = 1;i < arr.length; i++){
tmp = tmp ^ arr[i];
}
return tmp;
}
  • 二进制是0010 0000的十进制为0X32,第六位为1
if(n&0x32)
{
//处理的代码
}
  • 从低位到高位将n的第m的位置设为1
int setBitToOne(int n, int m)
{
return n | (1 << (m-1));
}
  • 计算二进制数中有多少个1

通过上面的学习,想必应该很快就知道这个这个题的思路。通过与1进行与运算,看结果是否为1,然后右移1位,逐步判断

def numberBit(n):
count = 0
while x:
count = count + (x&1)
x = x >> 1
return count

那么如果出现多个连续的0,此时就需要多做很多次移位的操作,可不可以考虑跳过连续的0?
ok,方法就是通过和(x-1)进行&运算,
看看代码实现

def numberBit(n):
count = 0
while n:
count = count + 1
n = n & (n-1)
return count
  • 求出m的n次方

面试官让你求解m的n次方且不能使用系统自带的pow函数,如果操作?二话不说,直接n个m相乘不就完事,代码如下

int pow(int x)
{
int temp = 1;
for(int i=1;i<=n;i++){
temp=temp*m;
}
return tmp;
}

代码虽然写出来了,但是对于面试而言显然是不能过关的,只好来一句:"还有其他的办法么"

以上题目貌似对于你们来说太简单了,还是得加点难度看几个高频且有点难度的题目

3 再看几个题目

  • 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

小白题解:

根据题目,线性时间复杂度为O(n),直接上Hash映射,但是空间复杂度为O(n),不怎么友好

这个时候就应该想到异或了XOR。为啥

  • 一个数和0做XOR运算等于本身
  • 一个数和本身做XOR运算等于0
  • XOR运算满足交换律和结合律
class Solution {
public int singleNumber(int[] nums) {
int ans = 0;
for(int num: nums) {
ans ^= num;
}
return ans;
}
}
  • 将二进制表示减到1的步骤数
  • 如果当前数字为偶数,则将其除以2
  • 如果当前数字为奇数则加上1

题目总是可以按上述规则将测试用例变为1
示例
输入:s = "10"
输出:1
解释:

"10" 表示十进制数 2 。
Step 1) 2 是偶数,除 2 得到 1

思路:

  • 计算最后一个1余第一个1之间有多少0
  • 只要存在0 就会因为从后往前第一个0前面的1的+1操作而变成1,这样每个0都会进行一次+1操作
  • 最后再加上第一次的+1操作 即s的长度+0的个数+1

代码实现

public int numSteps(String s) {
int zero = 0;
int i = s.length()-1;
while(i >= 0 && s.charAt(i) != '1')i--;
if(i == 0)return s.length()-1;
while(i > 0) if(s.charAt(i--) == '0')zero++;
return zero+s.length()+1;
}

4 位运算总结

  • 通过位运算能方便的获取某一位是1还是0
  • 异或特性:任何数和自身的异或结果为0;0和任意数的异或还是本省
  • 左移相当于乘以2,右移相当于除以2