一个数组中有两种数出现了奇数次,其他数都出现了偶数次,寻找这两个数

1) 在一个数字序列中,有一个数出现了奇数次,其他数都出现了偶数次,找到这个奇数次的数

2) 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,寻找这两个奇数次的数

1.异或运算:
简单来讲,就是相同为0,不同为1:
  1^1=0
  0^0=0
  1^0=1
  0^1=1
此外,异或运算符合两个性质:交换律和结合律,也就是说
a ^ b = b ^ a
a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;

2.问题分析
看过问题一的小伙伴都知道了异或运算的原理,在这里就不多说了。
我们还是将这个序列的所有数都异或一下,假设这两个奇数是a和b,那么就得到了一个数a^b.
那么问题来了,如果知道a和b分别是多少呢?
根据题干我们知道,a和b肯定不相等,因此,a^b != 0,既然它不等于0,那么在二进制上肯定有一位为1,我们根据上面提到的公式提取得到最右侧为1的那个数c。

此外,a^b != 0,且最右侧有一位为1,只有0和1才能异或出来,那么我们可以断定,a和b在这一位上,肯定是一个为0,一个为1.

我们都知道,这个数c有一个特征,只有一位为1,其他位置都为0.

现在我们让c和数组arr中的所有元素进行相与,只有两个位置都为1,才能相与出来1。因此,这样我们可以把数组序列分为两类,一类是在这一位上为0的值,一类是在这一位上为1的值。

最后,我们让(a^b)得到的值分别与这两类的值进行异或,消除掉那些偶数的值,剩下的就是这两个数。

3.如何获取最右边的1

寻找一个二进制数在最右侧的1,示例:
c = 0110100
从右往左看,c在倒数第三位上的数字为1,没错,我们就是要找这个数0000100,怎么做到呢?记住下面的这个公式:
result=c&(~c+1))

&运算: &运算中1&1=1,1&0=0,0&0=0

!c = 1001011
!c + 1 = 1001100
c&(~c+1) = 0000100
记住这个算式即可

4.代码如下:

    //1)在一个数字序列中,有一个数出现了奇数次,其他数都出现了偶数次,找到这个奇数
    public static void printNumber(int[] arr){
        int eor = 0;
        for (int cur : arr){
       //把这个数组所有的数进行异或运算,即可得到这个奇数次的数 eor
^= cur; } System.out.println(eor); }   
  //2) 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,寻找这两个数
public static void printTwoNumber(int[] arr){ int eor = 0; for (int cur : arr){ eor ^= cur; }
     //一个数“与” 上这个数 “取反加一” 就可以得到最右边的1
int rightOne = eor & (~eor + 1); int eor1 = 0;
     //获取到两个数中的其中一个(至于是哪一个不用管它,肯定会有一个)
for (int cur : arr){
       //用最右边的1这个数来区分, &运算中1&1=1,1&0=0,0&0=0
if ((cur & rightOne) != 0){ eor1 ^= cur; } } System.out.println(eor1 + " " +(eor1^eor)); }

 

posted @ 2022-07-04 14:06  初仰  阅读(468)  评论(0编辑  收藏  举报