程序员面试题精选100题(63)-数组中三个只出现一次的数字[算法]
题目:一个数组中有三个数字a、b、c只出现一次,其他数字都出现了两次。请找出三个只出现一次的数字。
分析:我们讨论了如何在一个数组中找出两个只出现一次的数字。在这道题中,如果我们能够找出一个只出现一次的数字,剩下两个只出现一次的数字就很容易找出来了。
如果我们把数组中所有数字都异或起来,那最终的结果(记为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++代码如下:
void getThreeUnique(vector
{
if(() < 3)
return;
int xorResult = 0;
vector
for(; iter != (); ++iter)
xorResult ^= *iter;
int flags = 0;
for(iter = n(); iter != (); ++iter)
flags ^= lastBitOf1(xorResult ^ *iter);
flags = lastBitOf1(flags);
// get the first unique number
int first = 0;
for(iter = n(); iter != (); ++iter)
{
if(lastBitOf1(*iter ^ xorResult) == flags)
first ^= *iter;
}
_back(first);
// move the first unique number to the end of array
for(iter = n(); iter != (); ++iter)
{
if(*iter == first)
{
swap(*iter, *(() - 1));
break;
}
}
// get the second and third unique numbers
getTwoUnique(n(), () - 1, unique);
}
int lastBitOf1(int number)
{
return number & ~(number - 1);
}
void getTwoUnique(vector
{
int xorResult = 0;
for(vector
xorResult ^= *iter;
int diff = lastBitOf1(xorResult);
int first = 0;
int second = 0;
for(vector
{
if(diff & *iter)
first ^= *iter;
else
second ^= *iter;
}
_back(first);
_back(second);
}
上文中getThreeUnique从数组中找出三个只出现一次的数字,而getTwoUnique从数组中找出两个只出现一次的数字。lastBitOf1实现分析中的函数f(n)的功能,它只保留数字n的二进制表示中的最后一位1,而把其他所有位都变成0。
在函数getThreeUnique中,我们通过第一个for循环把a、b、c三个数字异或的结果保存到xorResult中,接着在第二个for循环中求出f(x^a)^f(x^b)^f(x^c)并保存到变量flags中。在语句flags=lastBitOf1(flags)求出f(x^a)^f(x^b)^f(x^c)结果的二进制中最后一位是1的位。并根据这一数位求出第一个只出现一次的数字first。接着把first交换到数组的最后,并在数组的前n-1个数字中求出另外两个只出现一次的数字。
-
面试的邀请函优秀
邀请函分为婚庆邀请函、商务邀请函、会议邀请函等。在充满活力,日益开放的今天,邀请函与我们的关系越来越密切,拟起邀请函来就毫无头绪?以下是小编帮大家整理的面试的邀请函优秀,欢迎阅读,希望大家能够喜欢。面试的邀请函优秀1______先生/女士:您好!我公司人力资源部...
-
面试职位自荐信
我们眼下的社会,我们都可能会用到自荐信,写自荐信的时候要注意内容的完整。相信大家又在为写自荐信犯愁了吧!下面是小编精心整理的面试职位自荐信,欢迎大家分享。面试职位自荐信1尊敬的校领导:您好!首先感谢您在在繁忙的事物之中浏览我的求职自荐信,下面,我将对本身进...
-
面试的邀请函【热】
邀请函是在举办某项活动前,邀请别人来参加的书面邀约。在生活中,邀请函在活动中起到的作用越来越大,我敢肯定,大部分人都对拟定邀请函很是头疼的,下面是小编精心整理的面试的邀请函,欢迎大家分享。面试的邀请函1xxx先生/女士:您好!我公司人力资源部通过xxx收到您的简...
-
教师面试技巧[大全15篇]
教师面试技巧1教师资格证面试注意事项及常用小技巧1、提前进入考点(尤其是早晨7:30备考的考生提前做好充分的时间准备)2、注意自己的饮食、以及身体状态。3、穿丝袜的女生建议多留个备份(以防被不小心刮破等)4、每人至少带上两杆笔5、提前准备好衣服(提前熨好整理出...