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

题目描述:

一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。

思路:

1. 如果是数组中只有一个数字出现了奇数次,其他数字都出现偶数次,可以采用异或的方法,因为相同的数字异或结果是0,而0异或任何数字等于该数字本身。因此可以将数组中所有数字进行异或,最后剩下的就是只出现奇数次的数字。

2. 如果是找数组中只出现一次的数字,也可以用哈希表记录所有数字,参考第一个只出现一次的字符,区别是字符是有限个的256,所以空间效率是O(1)。而对于数字,空间效率是O(n),所以对于数字可以用异或更高效。

现在有这样的两个数字,参考上述思想,应该要将数组分为两个子数组,保证这两个数字分别在两个数组中,而其他数字都成对出现。这样就把问题分解为两个子问题,只要对两个子数组分别进行一次上述异或操作就可以了。

现在的关键是如何划分子数组。我们还是先将数组中所有元素异或,得到的结果就是要找的两个数字异或的结果,因为这两个数字不一样,因此结果肯定不为0,也即结果的二进制表示中至少有一个为1,从低往高找到第一个为1的位置n,我们就按照这一位是1还是0来对数组划分。第一个子数组中的数字第n位都为1,第二个子数组中的数字第n位都为0。因为相同数字的第n位也相同,所以相同的元素肯定会被划分到同一个子数组,而要找的两个数字的第n位肯定不相同(因为这一位的异或结果为1),所以两个数字会被分别划分到两个数组中,这样就达到了目的。

参考代码:

在线测试

https://www.nowcoder.com/practice/e02fdb54d7524710a7d664d082bb7811?tpId=13&tqId=11193&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking&tPage=2

AC代码

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        int result=0;//初始化为0
        for(int i=0;i<data.size();i++)
        {
            result=result^data[i];//计算所有数的异或
        }
        int n=0;//找到第一个为1的数位
        while((result&1)==0)
        {
            n++;
            result=result>>1;
        }
        *num1=0;
        *num2=0;
        for(int i=0;i<data.size();i++)
        {
            if(nIs1(data[i],n))
                *num1=(*num1)^data[i];
            else
                *num2=(*num2)^data[i];                
        }
    }
    //判断第n位是不是1
    bool nIs1(int num, int n)
    {
        num=num>>n;//直接右移n位
        return (num&1);
    }
};

猜你喜欢

转载自blog.csdn.net/u012991043/article/details/81839724