1. 进制转换
思路:
数制转化问题可以利用乘法除法进行运算并用栈或者其他数据结构进行存储,但是更高效的是能用就用位运算来做。例如将十进制转为16、8、2进制,都可以运用位运算来做,这里贴出java.lang.Integer中进制转化代码供借鉴和学习~
private static String toUnsignedString(int i, int shift){//i为待转换的整数,shift为要转换的进制需要与的位数
char[] buf = new char[32];
int mask = (1 << shift)-1; //要每次与的值,16进制是1111(15),8进制是111(7),2进制是1(1)
int pos = 32;//从后向前
do{
buf[--pos] = i & mask;
i >>>= shift;
}while(i != 0);
return new String(buf, pos, (32-pos));//此处可以根据实际情况改写。
}
2. 加法运算
剑指offer47_不用加减乘除做加法
思路:用^得到结果,用&得到进位。将进位左移,再循环做前两步。
注意问题:负数在内存以补码表示。因此,直接相加没有问题,可以得到正确结果。
public int AddWithoutOperator(int a, int b){
int rs = 0, int prog = 0;
do{
rs = a ^ b;
prog = (a & b) << 1;
a = rs;
b = prog;
}while(b != 0);
return a;
}
3. 简单位运算
leetcode201
https://leetcode.com/problems/bitwise-and-of-numbers-range/
Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all numbers in this range, inclusive.
For example, given the range [5, 7], you should return 4.
思路:如果正常将范围内数字一个一个与,时间复杂度比较高。因此,本题关键是找一些trick可以减少时间复杂度。
第一种方法:首先通过观察,如果数字范围跳跃了2,4,8,16..等2^i的时候,结果就为0,因此可以通过判断简化该类特征的数字范围与操作,其他在一个指数范围内的挨个与。
这样简化了部分,但是在一个指数范围内的还要挨个与。
第二种方法:利用m&(m-1)会将m的最右边的1清除的思想,每次判断m是否大于n,如果大于,说明还有该范围内的数字未做与操作,则继续消除m最右边1。若小于,说明该范围内的数字都已经与过了,跳出即可。这样会减少很多与的次数。
第三种方法:利用第一种方法思想,若数字最高位1的所在位数不同,则相与一定为0。因此,从左向右找到m和n第一个不同位,将其和此后所有清0即可。eg:[10111,11101]–>10000。很好理解,因为抛开左边第一位相同的1,后面[0111,1101]范围内的相与根据第一种方法思想一定是0,因此只剩第一位1可以保留。
用第三种方法实现的代码:
@Checked
public int rangeBitwiseAnd(int m, int n) {
int rs = m ^ n, pivot = Integer.MAX_VALUE;
while((pivot & rs) != 0) pivot <<= 1;
return pivot & m;
}