Java位运算的几个骚操作

前言

这段时间在看左神(左程云)的算法课,其中有讲到几个关于位运算的骚操作。在此记录下来,以备后查。

1、使用异或^实现交换

排序算法中,经常需要用到交换,常见方法如下:

void swap(int[] arr,int i,int j){
	int tmp=arr[i];
	arr[i]=arr[j];
	arr[j]=tmp;
}

但如果要求不使用临时变量,就地交换,咋办呢???
这里就需要位运算来表演了,代码如下:

void swap(int[] arr,int i,int j){
	arr[j]=arr[i]^arr[j];
	arr[i]=arr[i]^arr[j];
	arr[j]=arr[i]^arr[j];
}

初看很懵逼,右边仨一样的代码,就完成交换了???
细看就明白了,把arr[i]看成A,arr[j]看成B,整个方法其实就是A ^ B ^ A=B这个规律的变体。
优点:这种写法时间空间性能都更高,且看起来更有逼格
缺点:别人不一定能看懂,且要求A和B是不同的对象,否则结果恒等于0

2、找到唯一的奇数次的数

如题,在一个数组中,只有一个数出现了奇数次,其它数都出现了偶数次,该如果找到这个数?
答:全部异或,原因是0 ^ A=A,A ^ A=0。代码如下:

int findOdd(int[] arr){
	int eor=0;
	if(int i=0;i<arr.length;i++){
		eor^=arr[i];
	}
	return eor;
}

3、提取最右侧的1

如题,如何将一个整形的数,提取出其二进制编码最右侧的1。如int A=168,其二进制为00000000 00000000 00000000 10101000,提取最右侧的1的结果应该为00000000 00000000 00000000 000010000。
答:使用骚操作 A&((~A)+1)
理解:取反的操作,使得~A的高位与A完全相反。加1的操作,使得低位那些因为取反而得的1,重新进位变成0。而最右侧的1,经过取反(变成0)和进位(变成1)这两步操作,保持不变。

int getRightMost(int a){
	return a&((~a)+1);
}

好像很厉害,可是这有什么用呢?且看下一题

4、找到唯二的奇数次的数

如题,在一个数组中,只有两个不同的数出现了奇数次,其它数都出现了偶数次,该如果找到这两个数?
思路:假设A和B是要找的这两个数,那么先将数组全部异或的话,eor=A^B,因为A和B是不相同的两个数,那么eor的二进制表示中,必然存在包含1的位,这些1体现了A和B的差异(两者在这些位上只能有一个为0)。根据上一题的方法,找到eor最右侧的1,然后将整个数组,根据这个最右侧的1分成两类,将其中一类(如在该位上值为0)全部异或,即得到A。将A与eor异或,即得到B。代码如下

int[] findOdd2(int[] arr){
    // eor=A^B
	int eor=0;
	if(int i=0;i<arr.length;i++){
		eor^=arr[i];
	}
	//找到eor最右侧的1
	int rightMost=eor&((~eor)+1);
	//将arr根据最右侧的1分类,其中一类全部异或得到A
	int a=0;
	for(int i=0;i<arr.length;i++){
		if(arr[i]&rightMost==0){
			a=a^arr[i];
		}
	}
	//将A与eor异或得到B
	int b=eor^a;
	int[] res={a,b};
	return res;
}

评价:该题极为精巧,很能体现位运算的风骚气质
关于提取最右侧的1技巧的应用,还可以看下一题

5、求整数的二进制表示中,1的个数

思路:使用提取最右侧1的技巧,提取出一个1,累加器加1,然后抹去最右侧的1,直到该整数为0

int countBinary1(int a){
	int count=0;
	while(a!=0){
		int rightMost=a&((~a)+1);
		count++;
		a=a^rightMost;
	}
	return count;
}

评价:相比于遍历整数的32个位,性能更高。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值