题解

题目难度:较难

知识点:位运算

解题思路:在思考这道题是,首先想到的可能是按照数据的顺序,一个一个数比较,标记出只出现一次的数,输出这个数即可,但是这个过程的复杂度很高,复杂度为n^2。
在考虑这道题时,我们可以先思考一个简单版本:一个数组只有一个数字只出现一次,其他的数字都出现了两次。怎么查出这个数?
这个题目强调有一个数字出现一次,其他的出现两次。所以我们想到了异或运算的性质:任何一个数字异或它自己都等于0。所以我们只需要从头到尾异或所有的数,这是整个过程中,两个相同的数据就会全部抵消掉了,就只会剩下那个不同的数据。所以,一个数据组中如果只有一个不同,异或整个数组后就可以找出它。所以,对于这到原题,我们需要考虑的就是把这整个数据组分为两个数组,每个数组只包括一个不同的数据,单独异或这两个数据就可以了。
有了这个思路后,我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为这个数肯定不为0,找出它的1的位置,标记这个位置,然后按照这个完整的数据中的数据,在这个标记的位置是否为1,就可以分为两个数据组,每个数据组只含有一个不同的数据,然后就可以找出这两个数字了。

解题

这是一道考察位运算的题。两个相同的数进行异或其结果为0。所以当所有值进行了一次异或后,最后的结果就是这两个不同值的异或结果。然后通过与运算和位移运算,找到异或后结果为1的位置,此位置所有为0的进行异或和所有为1的异或就是结果。

#include <bits/stdc++.h>
using namespace std;
int a[1000001];
void getNumber(const int a[], int n, int&num1, int&num2)
{
    int OR= 0;
    int flag = 0;
    for(int i = 1; i <= n; i++)
    {
         OR ^= a[i];
    }
    while((OR & 1) == 0)
    {
        OR = OR >> 1;
        flag++;
    }
    for(int i = 1; i <= n; i++)
    {
        if((a[i] >> flag) & 1)
            num1 ^= a[i];
        else
            num2 ^= a[i];
    }
}
int main(void)
{
    int n = 0;
    while (~scanf("%d", &a[n + 1])) ++n;
    int p, q;
    getNumber(a, n, p, q);
    if (p > q) swap(p, q);
    printf("%d %d\n", p, q);
    return 0;
}