• 三个只出现一次的数


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

    分析:在博客http://zhedahht.blog.163.com/blog/static/2541117420071128950682/中我们讨论了如何在一个数组中找出两个只出现一次的数字。在这道题中,如果我们能够找出一个只出现一次的数字,剩下两个只出现一次的数字就很容易找出来了。

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

    我们可以证明异或的结果x不可能是a、b、c三个互不相同的数字中的任何一个。我们用反证法证明。假设x等于a、b、c中的某一个。比如x等于a,也就是a=a^b^c。因此b^c等于0,即b等于c。这与a、b、c是三个互不相同的三个数相矛盾。

    由于x与a、b、c都各不相同,因此x^a、x^b、x^c都不等于0

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

    接着我们考虑f(x^a)^f(x^b)^f(x^c)的结果。由于对于非0的n,f(n)的结果的二进制表示中只有一个数位是1,因此f(x^a)^f(x^b)^f(x^c)的结果肯定不为0。这是因为对于任意三个非零的数i、j、k,f(i)^f(j)的结果要么为0,要么结果的二进制结果中有两个1。不管是那种情况,f(i)^f(j)都不可能等于f(k),因为f(k)不等于0,并且结果的二进制中只有一位是1

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

    接下来我们证明x^a、x^b、x^c的三个结果第m位不可能都是1。还是用反证法证明。如果x^a、x^b、x^c的第m位都是1,那么a、b、c三个数字的第m位和x的第m位都相反,因此a、b、c三个数字的第m位相同。如果a、b、c三个数字的第m位都是0,x=a^b^c结果的第m位是0。由于x和a两个数字的第m位都是0,x^a结果的第m位应该是0。同理可以证明x^b、x^c第m位都是0。这与我们的假设矛盾。如果a、b、c三个数字的第m位都是1,x=a^b^c结果的第m位是1。由于x和a两个数字的第m位都是1,x^a结果的第m位应该是0。同理可以证明x^b、x^c第m位都是0。这还是与我们的假设矛盾。

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

    这种思路的C++代码如下:

    #include <iostream>
    #include <vector>
    using namespace std;
    //获取一个数二进制中从右到左,第一个1.其他均为0
    int lastBitOf1(int x)
    {
        return x&~(x-1);
    }
    //获取两个只出现一次的数
    void getTwoNum(vector<int>::iterator begin,vector<int>::iterator end, vector<int>&unique)
    {
        int xorResult=0;
        for(vector<int>::iterator iter=begin;iter!=end;iter++)
        {
            xorResult^=*iter;
        }
        int diff=lastBitOf1(xorResult);
        int first=0;
        int second=0;
        for(vector<int>::iterator iter=begin;iter!=end;iter++)
        {
            if(diff&*iter)
            {
                first^=*iter;
            }else{
                second^=*iter;
            }
        }
        unique.push_back(first);
        unique.push_back(second);
    }
    //获取三个只出现一次的数
    void getThreeNum(vector<int>&number,vector<int>&unique)
    {
        int xorResult=0;
        vector<int>::iterator iter=number.begin();
         for(;iter!=number.end();iter++)
        {
            xorResult^=*iter;
        }
        
        int flag=0;
        for(iter=number.begin();iter!=number.end();iter++)
        {
            flag^=lastBitOf1(xorResult^*iter);
        }
        flag=lastBitOf1(flag);
        //找到第一个只出现一次的数
        int first=0;
        for(iter=number.begin();iter!=number.end();iter++)
        {
            if(lastBitOf1(xorResult^*iter)==flag)
            {
                first^=*iter;
            }
        }
        unique.push_back(first);
        //将first移动到向量的末尾
        for(iter=number.begin();iter!=number.end();iter++)
        {
            if(*iter==first)
            {
                swap(*iter,*(number.end()-1));
                break;
            }
        }
        getTwoNum(number.begin(),number.end()-1,unique);
    }
    void print(vector<int>v1)
    {
        for(vector<int>::iterator iter=v1.begin();iter!=v1.end();iter++)
            cout<<*iter<<" ";
    }
    int main()
    {
        //cout << "Hello world!" << endl;
        int a[9]={1,2,3,4,5,6,2,4,6};
        vector<int> v1(a,a+9);
        vector<int> v2;
        print(v1);
        cout<<endl;
        getThreeNum(v1,v2);
        //print(v1);
        print(v2);
        return 0;
    }
  • 相关阅读:
    《JAVA高并发编程详解》-Thread start方法的源码
    《JAVA高并发编程详解》-Thread对象的启动
    作为程序员,我建议你学会写作
    【灵异短篇】这个夜晚有点凉
    JAVA中for与while关于内存的细节问题
    通过本质看现象:关于Integer受内部初始化赋值范围限制而出现的有趣现象
    【设计模式】抽象工厂模式
    【设计模式】工厂模式
    【设计模式】单例模式
    【设计模式】基本介绍
  • 原文地址:https://www.cnblogs.com/wft1990/p/7102708.html
Copyright © 2020-2023  润新知