首页 > 代码库 > 二进制和位运算中的异或

二进制和位运算中的异或

1、给出一个问题:给你一个整形数组,这个数组中除了一个数字只出现一次外,其他数字都只出现两次,求出那个只出现一次的数字?

要求:时间复杂度为O(n) , 空间复杂度为O(1)。


这个题目的难点在于空间复杂度的限制。


解法:一个数出现两个,两个数相同,而相等两个数异或的值为0 , 所以,我们只需要把整个数组的数都异或一遍,我们就能得到只出现了一次的那个数字


<span style="font-size:18px;">int get_one_num(int num[] , int n)
{
    int first = num[0] , i;
    for(i = 1; i < n; i++)
        first ^= num[i];
    return first;
}</span>


2、问题二:现在这个数组中,有两个数只出现了一次,求出这两个数字,且时间、空间复杂度不变。


如果根据第一个问题的解法,我们最后面也是得到了一个值,但这个值是那两个数字(只出现一次)的异或值。我们又不知道这个两个数字的任何一个,所以我们得不到这两个数字。


如果我们把数组分割成2个数组,每个数组中只含有一个只出现一次的数字,再调用问题一的解法,我们就能得到结果。

关键在于我们怎么来分割这个数组,且空间复杂度是O(1)?

首先我们从头到尾依次异或数组中的每一个数字,得到的结果就是两个只出现一次数字的异或结果,因为这两个数字肯定不一样,所以得到的结果数字肯定不是0,也就是这个二进制数字钟至少有一位是1 , 我们在这个结果数字中华找到第一个为 1的位的位置,记为第n为,我们就通过第n位是不是1,一把这个数组分割成一个数组。


代码

<span style="font-size:18px;">int find_one(int x)
{
    int p = 1;
    while(true)
    {
        if((p^x) < x)  break;
        p <<= 1;
    }
    return p;
}

void get_one_num(int num , int n)
{
    int first = num[0] , i;
    for(i = 1; i < n; i++)
        first ^= num[i];
    int one = find_one(first);

    int x = 0 , y = 0;
    for(i = 0; i < n; i++)
    {
        if((one^num[i]) < num[i])
            x ^= num[i];
        else y ^= num[i];
    }

    cout<<(x<y?x:y)<<" "<<(x>y?x:y)<<endl;
}</span>



二进制和位运算中的异或