Just do it
专注做自己的事,有想做的事就去做好了

分析题目

这题主要考察的是对亦或运算的理解和运用,亦或运算中a^a=0,0^a=a,所以偶数次的a亦或运算等于0;奇数次的亦或运算等于它本身。

所以通过遍历这个数组做亦或运算最后得到的肯定是这两个奇数的亦或运算。暂且将这两个奇数分别用a和b表示。那么最终结果就是a^b,暂且叫这个数c。

到这里我们得到了a^b。这里我们想要单独得到a和b要怎么操作呢?

我们通过观察可以知道,a^b的结果转化为二进制,出现1的位置肯定是不等的。接下来我们要利用这个规律来的到a,但是在此之前我们需要知道a^b的二进制结果哪个位置上是1呢?

这里我们需要知道另一个知识点:想提取出一个int类型的二进制的数的最右侧的1,该数值本身与上它的取反加一的值即a&(~a + 1)

顺带一提:一个数的取反加一等于负的这个数。 ~a + 1 == -a,所以 a&(~a + 1) ==  a&(-a)

通过这个我们可以到的一个数值,它的二进制只有一个1,其余的都是0。暂且叫这个数d。且a和b中在该位置必有一个是1。好,现在我们只需要找到在该位置是1的那个数。

因为d的二进制只有一位是1,其余的都是0。那么任何数‘&’上d要么是0,要么是d。我们再遍历数组,把每个元素和d做‘&’操作,如果结果还是d,我们用该数和c做‘^’操作,得到的最终结果必然是a或b中的一个。那么不管结果是a还是b,我们再和c做‘^’操作即可得出另一个值。

题解:

    public static int[] seekOddNum(int[] array){
        // 便利array 将a^b的结果赋值给eor
        int eor = 0;
        for (int j : array) {
            eor ^= j;
        }
        // rightrestNum提取eor的二进制位最右侧的1的值
        int rightrestNum = eor & (-eor);
        // 临时变量,最终其中一个奇数的结果赋值给a
        int a = eor;
        for (int j : array) {
            // &运算遇0为0,所以(rightrestNum & j)的结果要么是rightrestNum 要么是 0
            if (rightrestNum == (rightrestNum & j)) {
                // 因为其他数是偶数个,所以 eor^j最终结果必然是两个出现奇数次的其中一个,并赋值给了a
                a ^= j;
            }
        }
        // 得到a,b的值也就呼之欲出了
        return new int[]{a, eor^a};
    }

 

 

 

posted on 2025-02-22 18:44  Ireck  阅读(29)  评论(0)    收藏  举报