前言:
在计算机中,很多时候,位运算的效率要比普通的数学运算的效率高很多,因为计算机的本质就是二进制的。
今天我们来分享下,在面试中,出现的高频的位运算算法题。
题目一
题意: 给定一个数组,里面只有一个数字出现了一次,其他数字都出现了两次,找到这个出现一次的数字。思路: 没有接触过位运算的面试者,很有可能就想到了哈希表来实现,记录每种数字出现的次数,最后找到出现一次的即可。但是这种做法不但时间复杂度很高,还利用了额外的空间,那有没有更好的办法呢?答案就是异或运算。
异或运算的规则:
两个数字进行异或运算,如果不一样,就返回1, 如果一样,返回0。
举例子: 1 ^ 0 = 1 0 ^ 1 = 1 1 ^ 1 = 0 0 ^ 0 = 0
可以看出来,还是很简单的,这里,左神算法里面提到了一个更好的想法,他认为异或运算就是不进位的相加,大家可以品一品。
所以这道题目我们就可以对整个数组遍历,做异或运算,最后偶数个数的数字都会消掉变成0。最后就剩下哪个只出现1 次的数字了。代码如下
public int findOdd(int [] nums) {
int ans = 0;
for (int num : nums) {
ans ^= num;
}
return ans;
}
题目二
题意: 给定一个数组,有两个数出现了奇数次,其他的数字都是出现了偶数次,找到这两个数分别是什么前置知识:解决这道题目我们需要知道一个小知识点。
给定一个二进制数,你怎么能拿到它最右边的1.
举例: 0010 1100 -> 0000 0100
这个如何去实现呢? 其实我们可以通过对这个数取反后再 + 1 就可以得到了。
思路: 我们可以对这个数组遍历并异或一边,根据前面的知识,最后我们获得的数组num = a ^ b,因为其他偶数个的数字都消除掉了。
接着,我们拿到这个num 数字最右边的1, 假定这个数字是k, 那么这个K可以把整个数组分为两类,通过与运算取实现即可。然后我们用其中一类去异或运算num就可以得到其中一个数a,那么B也就出来了,看下代码。
public void findOdd(int [] nums) {
int eor = 0;
for (int num : nums) {
eor ^= num;
}
int rightOne = eor & (~eor + 1);
int a = 0;
for (int j : nums) {
if ((rightOne & j) == 0) {
a ^= j;
}
}
int b = eor ^ a;
}