找出三个只出现一次的数字 C语言实现

题目:一个数组中有三个数字abc只出现一次,其他数字都出现了两次。请找出三个只出现一次的数字。

分析:如果我们把数组中所有数字都异或起来,那最终的结果(记为x)就是abc三个数字的异或结果(x=a^b^c)。其他出现了两次的数字在异或运算中相互抵消了。

我们可以证明异或的结果x不可能是abc三个互不相同的数字中的任何一个。

由于xabc都各不相同,因此x^ax^bx^c都不等于0

我们定义一个函数f(n),它的结果是保留数字n的二进制表示中的最后一位1,而把其他所有位都变成0f(x^a)f(x^b)f(x^c)的结果均不等于0

f(x^a)^f(x^b)^f(x^c)的结果的二进制中至少有一位是1。假设最后一位是1的位是第m位。那么x^ax^bx^c的结果中,有一个或者三个数字的第m位是1

可以证明x^ax^bx^c的三个结果第m位不可能都是1

因此x^ax^bx^c三个数字中,只有一个数字的第m位是1。于是我们找到了能够区分abc三个数字的标准。这三个数字中,只有一个数字满足这个标准,而另外两个数字不满足。一旦这个满足标准数字找出来之后,另外两个数字也就可以找出来了。

#include<stdio.h>
static int lastBitOf1(int x)
{
    return x&-x;
}
static void getTwoUnique(int a[],int n,int b[])
{
    int xorResult=0;
    for(int i=0;i<n;++i)
        xorResult^=a[i];
    int diff=lastBitOf1(xorResult);
    int first=0;
    int second=0;
    for(int i=0;i<n;++i)
    {
        if(diff&a[i])
            first^=a[i];
        else
            second^=a[i];
    }
    b[1]=first;
    b[2]=second;
}
void getThreeUnique(int a[],int n,int b[])
{
    if(n<3)
        return ;
    int xorResult=0;
    for(int i=0;i<n;++i)
    {
        xorResult^=a[i];
    }
    int flags=0;
    for(int i=0;i<n;++i)
    {
        flags^=lastBitOf1(xorResult^a[i]);
    }
    flags=lastBitOf1(flags);
    int first=0;
    for(int i=0;i<n;++i)
    {
        if(flags==lastBitOf1(xorResult^a[i]))
        {
            first^=a[i];
        }
    }
    b[0]=first;
    for(int i=0;i<n;++i)
    {
        if(a[i]==first)
        {
            int t=a[i];
            a[i]=a[n-1];//数组的最后一个元素
            a[n-1]=t;
            break;
        }
    }
    getTwoUnique(a,n-1,b);
}
int main(int argc, char *argv[])
{
    int a[]={2,2,3,3,4,4,  5,6,7,  8,8};
    int b[3];
    getThreeUnique(a,11,b);
    for(int i=0;i<3;++i){
        printf("%d",b[i]);
        if(i+1!=3)
            printf(" ");
    }
    printf("\n");
    return 0;
}


结果是正确的

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值