求2个数的中间值
先说结论:int mid = (start + end) >>> 1
二分查找中我们需要根据 start
和 end
求中点,正常情况下加起来除以 2 即可。
int mid = (start + end) / 2
但这样有一个缺点,我们知道int的最大值是 Integer.MAX_VALUE ,也就是2147483647。那么有一个问题,如果 start = 2147483645,end = = 2147483645,虽然 start 和 end都没有超出最大值,但是如果利用上边的公式,加起来的话就会造成溢出,从而导致mid计算错误。
解决的一个方案就是利用数学上的技巧,我们可以加一个 start 再减一个 start 将公式变形。
(start + end) / 2 = (start + end + start - start) / 2 = start + (end - start) / 2
这样的话,就解决了上边的问题。
然后当时和同学看到jdk
源码中,求mid
的方法如下
int mid = (start + end) >>> 1
它通过移位实现了除以 2,但。。。这样难道不会导致溢出吗?
首先大家可以补一下 补码 的知识。
其实问题的关键就是这里了>>> ,我们知道还有一种右移是>>。区别在于>>为有符号右移,右移以后最高位保持原来的最高位。而>>>这个右移的话最高位补 0。
所以这里其实利用到了整数的补码形式,最高位其实是符号位,所以当 start + end溢出的时候,其实本质上只是符号位收到了进位,而>>>这个右移可以带着符号位右移,所以之前的信息没有丢掉。
但>>有符号右移就会出现问题了,事实上 JDK6 之前都用的>>,这个 BUG 在 java 里竟然隐藏了十年之久。
大小写转换
结论:chars[i] ^= (1 << 5);
使用异或运算转换字母大小写。
我们发现大写字符与其对应的小写字符的 ASCII 的差为 32,32 这个值如果敏感的话,它是 2^5
,在编程语言中,可以表示为 1 << 5。而
变换大小写这件事等价于:
1、如果字符是小写字符,减去 32 得到大写字符;
2、如果字符是大写字符,加上 32 得到小写字符。
而这两者合并起来,就是给这个字符做一次不进位的加法,即异或上 1 << 5。