前言
java程序员只有10种人,一种懂二进制,一种不懂。呵呵~好冷。一、java的进制表示
- 二进制: 0b_____, 使用0b开头
- 八进制: 0_____, 使用0开头【对,如果开头有0就是八进制,没有0就是十进制,简单粗暴】
- 十六进制:0x_____, 使用0x开头。
二、java进制转换方法
1.其他进制转化为二进制字符串
这里说的转化基本都是转为其他进制字符串或者其他进制字符串转化十进制数值。
PS:进制的英文: Radix
数值转其他进制:
- 转二进制:Integer.toBinaryString(i);
- 转八进制:Integer.toOctalString(i);
- 转十六进制:Integer.toHexString(i);
2. 其他进制字符串转为十进制数值:
Integer.valueOf(String numstr, int radix); //返回Integer对象
或者
Integer.parseInt(String numstr, int radix); //返回int值
必须要标注转为几进制,否则八进制也会当做十进制转,即传递双参,第二个参数为进制;
另外,不要使用上节提到的记法,否则会抛出异常NumberFormatException
。
三、java关于数位的运算符
- &
将int值的转化为二进制,进行按位与
如,下面有个例子,需要计算int值的最低位是不是1,做法是
(i & 1) == 1;
1的二进制是00000001
, 因此任何数位与1相与高位都会变成0,只有最低位会保持,因此若i的最低位为1,则最后也会变成00000001
, 即为1。
同理,想要截取二进制的哪几位,就让他与哪几位为1的二进制相与。
注意:&的优先级没有 比较运算符==高,因此必须加上括号。
- | 按位或。
或的性质是全0保持数值原型;或者设置某些位置为1让原数值的这些位置变成1。
用的比较少。
- ^ 按位异或
这个符号的骚操作就多了:
- 与0异或,可以让原数值所有位保持原样;
- 与1异或,可以让所有位翻转
- 交换两个数值:
A = A ^ B; B = A ^ B; A = A ^ B;
PS:不过最好别这么写,否则会经常挨打。
话虽如此,因为位运算是最高效的,因此这种方法的速度是最快的。。。。
- ~ 按位取反
这个是单运算符。
5.<< 左移
朝左边就是左移,很好记
左移会在最低位补上0,不涉及最高位的正负号操作【无需考虑】。
左移一次可以将原数值 * 2
不过若是面试官问你左移一位是不是和 *2等价,必须答不是,还是那句:位运算的速度远高于数值运算。
- >> 带符号右移
右移需要考虑到正负号。
> > i 表示将二进制表示向右移动i位,并且按照原来数值的正负在最高位补上0或者1.
- >>> 无符号右移
右移需要考虑到正负号。
> >> i 表示将二进制表示向右移动i位,并且总是在最高位补0.
算法题中这种用的比较多。
四、几个例题
- 剑指15
请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
思路:
将i与1按位与,若为1,count++,并且i右移一位。
public int hammingWeight(int n) {
int count = 0;
while (n != 0) {
if ((n & 1) != 0) {
count++;
}
n >>>= 1;
}
return count;
}
可以优化的地方:
n & 1 == 1就++, 故可以直接简写为
count += n & 1;
//装逼如风,常伴吾身。其实是抄来的。
其实从这里也可以看出, &的优先级别比+更高
第二种解法,既然可以让前一位数右移,自然也能让1左移,其他仍然是一样的逻辑。
结束条件分析:
while(flag > 0) …
经过测试,我发现1左移31位就会变成负数,这应该是因为int类型占据4Byte,而首位表示符号位,左移31次之后,1这个数字就到达了符号位,因此这个数就变成了负数。
而测试数字也是int类型,显然也满足32位,所以1左移31位就可以检查完所有数位了,但是如果原数值最高位为1,需要出去多比较一次。
public int hammingWeight(int n) {
int count = 0;
int flag = 1;
while (flag > 0) {
if ((n & flag) != 0) {
count++;
}
flag <<= 1;
}
if ((n & flag) != 0) {
count++;
}
return count;
}
第三种解法: 将一个数减1,就是将这个数的**二进制最后一个1变成0,若这个数最后一个1后面还有0,就会把这些0变成1** > 如,1000 - 1 = 0111 这是,若将原数字i与i - 1相与,就会得到**i最后一位1变成0的数字**,我们称之为i',
这个算法就是求这样消去最后一个1的操作能进行几次。
public int hammingWeight(int n) {
int count = 0;
while (n != 0) {
count++;
n &= (n - 1);
}
return count;
}
最后这个解法的n & (n - 1)
的操作是进制算法中常见的做法:
如:
(1)求一个数n是不是2的整数次方。
若一个数是2的整次方,则这个数必有且只有一位为0.
因此,直接返回return n != 0 && n & (n - 1) == 0
即可。
(2)计算一个m的二进制要改变几位可以得到n的二进制。
- 求m ^ n;
- 按照上面的算法求1的个数
因为只有两者不一样的才是1,改变多少位成为n就是统计m与n不一样的数位。
public int hasNBits(int m, int n) {
int result = m ^ n;
int count = 0;
while (result != 0) {
result &= (result - 1);
count++;
}
return count;
}