在Java位运算总结-leetcode题目博文中总结了Java提供的按位运算操作符,今天又碰到LeetCode中一道按位操作的题目
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.
题意:
给出m和n,将m到n之间所有的数字与一遍返回结果(包括m和n)
解题思路:
题目乍一看很简单嘛,循环遍历m到n按位与一遍返回结果就完了啊,但是将这种解法放入LeetCode会发现超时,那么就需要我们去总结规律。
例如题目给出的5,6,7二进制
0101 0110 0111
通过观察我们会发现我们只要看数字的高位即可,后面的数字会相互约掉
再比如
011000 0110001 0110010 0110011
我们发现貌似只要查看所有数字的高k位即可
再比如
01100 01101 01110 01111 10000
我们比较发现如果m和n的位数不相同就会返回0
规律如下:
给出m和n,求出m和n相同的高k位(这里m和n为整型变量,那么按照32位来看待,前面的0也算作高位比较),那么保留高k为其余位置0即为最后结果。
代码如下:
一开始我选择从低位遍历,记录需要舍弃的低l位
代码1
1 public int rangeBitwiseAnd(int m, int n) { 2 if (n == m) 3 return m; 4 int h = 0; 5 int l = 0; 6 int mm = m; 7 while (m > 0 || n > 0) { 8 if ((m & 1) == (n & 1)) { 9 m >>= 1; 10 n >>= 1; 11 ++h; 12 } 13 else { 14 m >>= 1; 15 n >>= 1; 16 l += h; 17 ++l; 18 h = 0; 19 } 20 } 21 if(h==0)return 0; 22 return mm & (0x7fffffff << l); 23 }
通过后查看代码其实直接从高位开始遍历到第一个不相同的位停下即可找到高k位,代码如下
代码2
1 public int rangeBitwiseAnd2(int m, int n) { 2 if(m==n) return m; 3 int i; 4 for(i = 31;i>0;i--){ 5 if((m&(1<<i))!=(n&(1<<i))) 6 break; 7 } 8 return m&(~((1<<i+1)-1)); 9 }
代码简洁了很多,也比原来的快了很多
题目再次利用了Java中的移位操作,记录下做题中遇到的二进制的几个问题:
1.整数x求-x,从二进制角度看是将x按位取反加1
2.左移正负数都是低位补0,右移正数高位补0负数补1,而如果使用Java无符号右移>>>则正负高位都补0
3.将整数按照二进制整个翻转可以利用类似归并排序的分治思想
(1) 整数32为分成16和16先翻转一下
(2) 再翻转前16位中的前8位和后8位,再翻转后16位中的前8位和后8位
。。。以此类推,总共需要Log232 = 5次
代码如下:
1 public int reverseBit(int n) { 2 n = ((n & 0xffff0000) >>> 16) | ((n & 0x0000ffff) << 16); 3 n = ((n & 0xff00ff00) >>> 8) | ((n & 0x00ff00ff) << 8); 4 n = ((n & 0xf0f0f0f0) >>> 4) | ((n & 0x0f0f0f0f) << 4); 5 n = ((n & 0xcccccccc) >>> 2) | ((n & 0x33333333) << 2); 6 n = ((n & 0xaaaaaaaa) >>> 1) | ((n & 0x55555555) << 1); 7 return n; 8 }
注意这里是将整个32位全部翻转,相当于32位倒过来看。
举例,按照4位来看
1110
(1)将前2位和末2位翻转 10 11
(2)将第一部分对调,第二部分对调得到结果 01 11