计算机CPU中ALU是由逻辑门构成的,非常善于位运算。因此,在程序中使用位运算,经常能够加快程序运行。另外,掌握一些位运算的相关知识也能增加一些编程的乐趣。
位运算经常使用到异或运算和与运算。异或运算符合交换律、结合律,它的特点是相同为0,不同为1。与运算是同为1则为1。
在这里举几个跟位运算相关的例子:
1、**两个数比较大小
2、数组中出现奇数次的数
3、找出数组中两个出现奇数次的数
4、交换两个变量的值
详细内容
1、两个数比较大小
描述:给定两个整数a和b,用位运算比较两个数的大小,并将较大的数返回。
分析:首先计算a和b的差值c。通过判断c的符号来得到它们中较大的值。
示例代码:
public int sig(int c) {
return ((c >>> 31) & 1) == 0 ? 1 : 0;
}
public int nsig(int c) {
return sig(c) == 0 ? 1 : 0;
}
public int getMax1(int a, int b) {
int c = a - b;
int sigc = sig(c);
int nsigc = nsig(c);
return a * sigc + nsigc * b;
}
总结:直接相减可能会出现越界的问题,如何处理?
2、数组中出现奇数次的数
描述:一个整数数组中有一个数出现了奇数次,实现一个O(n)复杂度的算法找到这个值。
分析:利用异或运算相同则为零的性质,来找到数组中出现奇数次的值。
示例代码:
public int findOdd(int[] A, int n) {
if (A == null || A.length < n) {
return -1;
}
int ret = 0;
for (int i = 0; i < n; i++) {
ret = ret ^ A[i];
}
return ret;
}
总结:待整理
3、找出数组中两个出现奇数次的数
描述:一个整数数组中有两个数出现了奇数次,实现一个O(n)复杂度的算法找到这两个值。返回这两个值,并且较小的值在前。
分析:首先遍历这个数组,可以得到这两个数的异或值e。设e的二进制形式最右侧1的位置表示的数为k。因为e中logk位置为1,那么这两个数中必有一个数的二进制形式在logk位置是1。有了这个性质,再遍历这个数组,e0只和数组中二进制形式logk位置为1的数做异或操作,那么e0就是出现奇数次的数。然后e异或e0的结果就是另一个出现奇数次的整数。
示例代码:
public int[] findOdds2(int[] arr, int n) {
int e = 0, e0 = 0;
for (int curNum : arr) { e ^= curNum; }
int rightOne = e & (~e + 1);//快速求得右侧第一个1出现的位置
for (int cur : arr) {
if ((cur & rightOne) != 0) {
e0 ^= cur;
}
}
int small = Math.min(e0, (e ^ e0));
int big = Math.max(eOhasOne, (eO ^ eOhasOne));
return new int[] { small, big };
}
总结:待整理
4、交换两个变量的值
描述:请编写一个算法,不用任何额外变量交换两个整数的值。 给定一个数组num,其中包含两个值,请不用任何额外变量交换这两个值,并将交换后的数组返回。
分析:
a’=a^b;
b’=a’^b=a^b^b=a;
a’=a’^b’=a^b^a^b^b=b;
示例代码:
public int[] getSwap(int[] num) {
if (num == null || num.length < 2) { return null; }
num[0] = num[0] ^ num[1];
num[1] = num[0] ^ num[1];
num[0] = num[0] ^ num[1];
return num;
}
总结:待整理