文章目录
- 前置知识
- 1. 交换两个数
- 2. 比较两个数的大小
- 3. leetcode268 寻找缺失的数字
- 4. leetcode136 只出现一次的数字
- 5. leetcode260 只出现一次的数字|||
- 6. leetcode137 只出现一次的数字||
- 7. 2/3的幂
- 8. 大于等于该数字的最小2的幂
- 9. leetcode201 数字范围按位与
- 10. 位运算中分治法举例
前置知识
1. 交换两个数
因为我们的异或运算遵循的交换律和结合律, 所以我们可以写出下面的这一段代码
public void swap(int[] arr, int i, int j) {
//注意这里的 i != j
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
请注意这里面的 i != j 因为如果相等的话, 会直接把这一块区域置为0
2. 比较两个数的大小
/**
* 不包含比较相关的逻辑运算符来得到两个数当中的最大值
*/
//flip意为反转 --> 此时传入的n保证为 1 / 0 --> 如果是1, 就转化为0 , 如果是0, 就转化为1
private int flip(int n) {
return n ^ 1;
}
//获取一下某个数字的符号,如果是非负的数字就返回一个1, 如果是负数就返回0
private int sign(int n) {
return flip(n >>> 31);
}
//下面我们给出两种判断的方法 --> 第一种有可能会溢出, 第二种不会溢出
//核心的逻辑就是设置 returnA , returnB 来规定什么时候返回a, 什么时候返回b(两个return一定是互斥的才可以)
public int getMax1(int a, int b) {
//c可能会溢出
int c = a - b;
int returnA = sign(c);
int returnB = flip(returnA);
return a * returnA + b * returnB;
}
//下面的这个方法是不会溢出的
public int getMax2(int a, int b) {
//c依然可能会溢出
int c = a - b;
//下面判断一下a b c的符号
int signA = sign(a);
int signB = sign(b);
int signC = sign(c);
//判断a b 符号是不是相同的
int diffAB = signA ^ signB; //不同返回1
int sameAB = flip(diffAB); //相同返回1
//什么时候进行a的返回
//--> 1. ab不同号且a为非负
//--> 2. ab同号(此时不可能会溢出)且signC == 1
int returnA = diffAB * signA + sameAB * signC;
int returnB = flip(returnA);
return a * returnA + b * returnB;
}
上述的代码逻辑和之前的那个其实是一致的
就是加入了一些是否越界的判断(到底什么时候returnA)
3. leetcode268 寻找缺失的数字
class Solution {
public int missingNumber(int[] nums) {
//说白了这个题就是异或运算的性质
int eor = 0;
for(int elem : nums){
eor ^= elem;
}
int eorN = 0;
for(int i = 0; i <= nums.length; ++i){
eorN ^= i;
}
return eorN ^eor;
}
}
4. leetcode136 只出现一次的数字
class Solution {
public int singleNumber(int[] nums) {
int eor = 0;
for(int element : nums){
eor = eor ^ element;
}
return eor;
}
}
5. leetcode260 只出现一次的数字|||
class Solution {
public int[] singleNumber(int[] nums) {
int eor = 0;
for(int elem : nums){
eor ^= elem;
}
int n = eor & (~eor + 1);
int eorN = 0;
for(int elem : nums){
if((elem & n) == 0){
eorN ^= elem;
}
}
return new int[]{eorN, eor ^ eorN};
}
}
6. leetcode137 只出现一次的数字||
class Solution {
public int singleNumber(int[] nums) {
//首先进行的是统计每一个数位上的1的个数
int[] hash = new int[32];
for(int elem : nums){
for(int i = 0; i < 32; ++i){
hash[i] = hash[i] + ((elem >>> i) & 1);
}
}
//统计1的分位个数完毕, 开始还原数字
int ans = 0;
for(int i = 0; i < 32; ++i){
if(hash[i] % 3 != 0){
ans = ans | (1 << i);
}
}
return ans;
}
}
7. 2/3的幂
class Solution {
/**
* 判断一个数字是不是2的幂
* @param n
* @return
*/
public boolean isPowerOfTwo(int n){
return n > 0 && (n & (~n + 1)) == n;
}
/**
* 判断一个数字是不是3的幂(直接找到int范围内3的最大的幂是多少
* @param n
* @return
*/
public boolean isPowerOfThree(int n){
return n > 0 && 1162261467 % n == 0;
}
}
8. 大于等于该数字的最小2的幂
class Solution{
public int nearTwoPower(int n){
if(n <= 1){
return 1;
}
n--;
n = n | (n >>> 1);
n = n | (n >>> 2);
n = n | (n >>> 4);
n = n | (n >>> 8);
n = n | (n >>> 16);
return n + 1;
}
}
9. leetcode201 数字范围按位与
class Solution{
public int rangeBitswiseAnd(int left,int right){
while (left < right) {
right = right - (right & (~right + 1));
}
return right;
}
}
10. 位运算中分治法举例
class Solution{
/**
* 翻转二进制的状态
* 比如一个数的二进制位是 : 0001101101010010 --reverse--> 010010101011000
* 分析 : 正常的解法就是定义一个ans, 看到哪一位上有i你就或上去一个1就行了, 这里我们不在多说, 有点简单
* 我们重点说一下位运算分治的思路(从两个一组翻转 --> 四个一组 --> 八个一组.....)
* 假如有一个序列 a b c d e f g h
* 我们翻转的过程: 1. b a d c f e h g (1v1翻转)
* 2. d c b a h g f e (2v2翻转)
* 3. h g f e d c b a (4v4翻转)
* 下面推广到代码上 :
* 1. a b c d e f g h & 0 1 0 1 0 1 0 1 ==> 0 b 0 d 0 f 0 h (1)
* a b c d e f g h & 1 0 1 0 1 0 1 0 ==> a 0 c 0 e 0 g 0 (2)
* (1) << 1 | (2) >>> 1 ==> b a d c f e h g
* 下面的过程以此类推
* @param n
* @return
*/
public int reverseBits(int n){
n = ((n & 0x55555555) << 1) | ((n & 0xaaaaaaaa) >>> 1);
n = ((n & 0x33333333) << 2) | ((n & 0xcccccccc) >>> 2);
n = ((n & 0x0f0f0f0f) << 4) | ((n & 0xf0f0f0f0) >>> 4);
n = ((n & 0x00ff00ff) << 8) | ((n & 0xff00ff00) >>> 8);
n = ((n & 0x0000ffff) << 16) | ((n & 0xffff0000) >>> 16);
return n;
}
/**
* 计算一个数字的二进制位中有几个1
* 还是用的位运算分治的方法
* 思路分析 : 假如数字的二进制位是 1 0 1 1 0 1 0 1
*
* @param n
* @return
*/
public int cntOnes(int n){
n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff);
n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff);
return n;
}
}