每一个数在电脑中的存储都是以二进制的方式,比如Java中的int型便占据32位二进制。这是什么意思呢,比如说 int 型的数值2 ,转换为二进制便是00000000 00000000 00000000 0000010
我是在亦或相关的算法题中用到这个方法,下面将题目分享给大家:
题目:数组中有两种数出现基数次,其他数都是出现偶数次。问怎么找出这两种数?
解决方法如下:
@Test
public void quest2() {
//假设数组如下
int[] arr = {2, 2, 2, 2, 3, 3, 3, 3, 1, 4};
//设置eor
int eor = 0;
for (int cur : arr) {
eor ^= cur;
}
//这时候的eor = a ^ b
// eor ! = 0 说明 二进制上某一位一定不为 0
// rightOne 为 eor 中最右的1
int rightOne = eor & (~eor + 1);
//_eor (eor') 与rightOne同位都是1的所有数相与就是 a、b中的一个
int _eor = 0;
for (int cur : arr) {
if ((cur & rightOne) == 0) {
_eor ^= cur;
}
}
eor = eor ^ _eor;
System.out.println(eor + "," + _eor);
}
首先是让eor与数组中的每一个数都亦或过去,得到的eor便是两个基数个数的值亦或的结果即a^b。
//设置eor
int eor = 0;
for (int cur : arr) {
eor ^= cur;
}
这是大家比较容易想到的,但是接下来要怎么分离出a、b来呢?
我们可以知道eor=a^b且eor!=0,那么将eor转换为二进制时,起码有一位上a为0而b不为0,这样这一位的亦或结果便是1。那么我们就可以根据这一位的不同,再将数组中这一位数上为1的元素都亦或一遍过去,最终我们就可以得到一个基数次元素。(如果对亦或操作不太理解,可以先参考这一篇文章:亦或运算)
那么首先,我们就要先获得到eor在二进制上某一位上为1的值。这里便通过二进制运算获得,如下代码 eor & (~eor + 1) 便是得到 eor 二进制上最靠右侧的1。
// rightOne 为 eor 中最右的1
int rightOne = eor & (~eor + 1);
我们就可以通过这个数来获得数组中二进制这一位上也为1的元素。
if ((cur & rightOne) == 0)
那么本题的难点也就解决了,这一次循环亦或得到的值便是其中的一个基数次元素,再通过与第一次循环得到的eor亦或,得到的就是另一个的基数次元素。