剑指offer---数组中只出现一次的数字

//除了两个数字 A和B不相等 所以异或完以后的C C为1的某一位上 A和B在这一位上肯定是相异的
//不然不会异或为1  所以按这一位上为零或者为1来把数组分为两个 
//因为同一个数是不可能被分到两个组的
//简化为只有一个数字外 其余都出现两次的这个问题


class Solution 
{
public:
    int find(int num)
    {
        int result=0;
        while(((num&1)==0)&&result<32)
        {
            num=num>>1;
            ++result;
        }
        
        return result;
    }
    
    int panduan(int a,int weiyi)
    {
        a=a>>weiyi;
        
        if((a&1)==1)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

    
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) 
    {
        if(data.size()<2)
        {
            return ;
        }
        
        //依次异或 留下的就是两个不同的数异或的结果
        int temp=0;
        for(int i=0;i<data.size();++i)
        {
            temp^=data[i];
        }
        
        //找出某一个为1的位置; 作为判据分组; 这里需要两个函数
        int weizhi=find(temp);
        
        for(int i=0;i<data.size();++i)
        {
            if(panduan(data[i],weizhi)==1)
            {
                num1[0]^=data[i];
            }
            else
            {
                num2[0]^=data[i];
            }
        }
        
        
        
    }
};

 

推广:数组A中,除了某一个数字x之外,其他数字都出现了三次,而x出现了一次

我们换一个角度来看,如果数组中没有x,那么数组中所有的数字都出现了3次,在二进制上,每位上1的个数肯定也能被3整除。如{1, 5, 1, 5, 1, 5}从二进制上看有:

1:0001

5:0101

1:0001

5:0101

1:0001

5:0101

二进制第0位上有6个1,第2位上有3个1.第1位和第3位上都是0个1,每一位上的统计结果都可以被3整除。而再对该数组添加任何一个数,如果这个数在二进制的某位上为1都将导致该位上1的个数不能被3整除。因此通过统计二进制上每位1的个数就可以推断出x在该位置上是0还是1了,这样就能计算出x了。

推广一下,所有其他数字出现N(N>=2)次,而一个数字出现1次都可以用这种解法来推导出这个出现1次的数字。

posted @ 2017-07-31 21:19  双马尾是老公的方向盘  阅读(160)  评论(0编辑  收藏  举报