本文主要列举与、或、异或、等位运算符的一些奇巧淫技,顺便把用到这些技巧的算法题列出来讲解。
异或
// 异或底层原理,相同为0,不相同为1.
1 ^ 1 = 0
1 ^ 0 = 1
// 所以有任意数异或0为本身,相同两个数异或为0.
a ^ 0 = a
a ^ a = 0
// 根据上面的公式得
a ^ a ^ b = b
下面看道用到此技巧的算法题目。
1、任何数和 0 做异或运算,结果仍然是原来的数,即 a ^ 0 = a。
2、任何数和其自身做异或运算,结果是 0,即 a ^ a = 0。
3、异或运算满足交换律和结合律,即 a ^ b ^ a = b ^ a ^ a = b ^ (a ^ a) = b ^ 0 = b
思路:对数组全部元素进行异或操作,出现次数为偶数的都相互抵消为0,因为只有一个元素出现一次,其他元素都出现两次,也就是相当于 0 ^ a = a,最后运算结果就是只出现一次的元素。
代码如下:
class Solution {
public int singleNumber(int[] nums) {
int a = 0;
for (int num : nums) {
a ^= num;
}
return a;
}
}
进阶版:给定一个数组长度为K,其中一个元素重复两次,其余元素只出现一次,数组元素范围[1,K],找到重复两次的这个元素;这道题就留给各位解答吧。
n & (n-1)
这个公式就是布赖恩·克尼根算法,目的就是消除n二进制表示中最后的一个1。
下面看几道用到此技巧的算法题目。
思路:对于两个数对应二进制不同的位置的数目我们可以先对两个数进行异或 x ^ y = xor,之后再统计xor二进制位1的个数。
代码如下:
//思路1
class Solution {
public int hammingDistance(int x, int y) {
//清除相同进制位;
x ^= y;
//获取 1 的个数;
int count = 0;
while (x!=0){
x&=(x-1);
count++;
}
return count;
}
}
//思路2,使用java内置函数,统计1的个数。
class Solution {
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}
}
思路:只要是2的幂,那么这个数它的二进制位只有一个1。
// 1 = 0b 0001
// 2 = 0b 0010
// 4 = 0b 0100
class Solution {
public boolean isPowerOfTwo(int n) {
if(n<=0){
return false;
}
return (n & (n-1))==0;
}
}
思路:只要是4的幂,那么这个数它的二进制位只有一个1,不同2的幂的是,它只能是奇数位为1。
我们可以将 n & 0x55555555,如果结果不为0,则n就为4的幂。
// 1 = 0b 0000 0001
// 4 = 0b 0000 0100
// 16 = 0b 0001 0000
class Solution {
public boolean isPowerOfFour(int num) {
if(num <= 0 || (num & (num-1)) != 0){
return false;
}
return (num & 0x55555555) != 0;
}
}
为什么要 n & 0x55555555?其实很简单。
/* 5 = 0b 0101
* 2进制转16进制,2进制每4位等于16进制1位。
* 0x5555 5555 = 0b 0101 ... 0101 (重复8次)
* 所以如果是要偶数位,我们可以先设 n = 0b 1010,n = 10,x = 0x AAAA AAAA
以上便是一些简单的位运算技巧,下期继续更新位运算更多高级解题方法。喜欢的点个订阅吧!