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

转自:http://zhedahht.blog.163.com/

题目:一个数组中有三个数字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++代码如下:

void getThreeUnique(vector<int>& numbers, vector<int>& unique)

{

if(numbers.size() < 3)

return;

int xorResult = 0;

vector<int>::iterator iter = numbers.begin();

for(; iter != numbers.end(); ++iter)

xorResult ^= *iter;

int flags = 0;

for(iter = numbers.begin(); iter != numbers.end(); ++iter)

flags ^= lastBitOf1(xorResult ^ *iter);

flags = lastBitOf1(flags);

// get the first unique number

int first = 0;

for(iter = numbers.begin(); iter != numbers.end(); ++iter)

{

if(lastBitOf1(*iter ^ xorResult) == flags)

first ^= *iter;

}

unique.push_back(first);

// move the first unique number to the end of array

for(iter = numbers.begin(); iter != numbers.end(); ++iter)

{

if(*iter == first)

{

swap(*iter, *(numbers.end() - 1));

break;

}

}

// get the second and third unique numbers

getTwoUnique(numbers.begin(), numbers.end() - 1, unique);

}

int lastBitOf1(int number)

{

return number & ~(number - 1);

}

void getTwoUnique(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);

}

上文中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个数字中求出另外两个只出现一次的数字。

时间: 2024-08-05 11:17:17

【转】一个数组中有三个数字a、b、c只出现一次,其他数字都出现了两次。请找出三个只出现一次的数字。的相关文章

C语言实现一个int类型数组里除了两个数字之外,其他的数字都出现了两次,找出这两个数字

题目是这样叙述的:在一个数组中除两个数字只出现1次外,其它数字都出现了2次, 要求尽快找出这两个数字. 要求:时间复杂度为O(N),空间复杂度为O(1). 请看我的分析:将这道题简单化:一个数组中只有一个数字出现一次,其他数字都是成对出现的,这时我们可以根据异或运算符的特性:A^B^A = B; 0 ^ A = A:我们可以将这个数组的全部元素依次做异或运算,最终结果就是那个只出现一次的数字.不会的可看本人(2019-04-04)那天的博客 如果这个数组中出现两个不同的数字,而其他数字均出现两次

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

题目:一个数组中有三个数字a.b.c只出现一次,其他数字都出现了两次.请找出三个只出现一次的数字. 分析:如果我们把数组中所有数字都异或起来,那最终的结果(记为x)就是a.b.c三个数字的异或结果(x=a^b^c).其他出现了两次的数字在异或运算中相互抵消了. 我们可以证明异或的结果x不可能是a.b.c三个互不相同的数字中的任何一个. 由于x与a.b.c都各不相同,因此x^a.x^b.x^c都不等于0. 我们定义一个函数f(n),它的结果是保留数字n的二进制表示中的最后一位1,而把其他所有位都变

C++数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。(牛客剑指offer)

///////////////////////////////////////////////////////// //数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字. //例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}. //由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. #include <iostream> using namespace std; int Grial(int a[],int n) { if(n==0)return -1;

【c语言】数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字

题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}, 由于数组中数字2出现了5次,超过数组的长度的一半,因此输出2 一种办法是先把数组排序,那么超过一半的元素一定是数组最中间的元素. 第二种办法比较抽象,设一个变量保存当前值,设一个次数,当前值与下一个值进行比较,如果相等,次数加一,如果不相等,次数减一,如果次数减到0了还是不相等,就把当前值替换掉.代码如下: #include <stdio.h> #includ

一个数组中有65535个数不重复的大于0的整数(即:0~~65535内所有不重复的整数,数序是杂乱无章的), 用最快的方式排序

备注:如果这个问题你考虑到用元素对比就大错特错了 当然这个算法还不是最优的,不能代表普遍性.但可以引申,总体还是这个思路,无非就是“填空”操作 public class OrderLink { /** * 一个数组中有65535个数不重复的大于0的整数(即:0~~65535内所有不重复的整数,数序是杂乱无章的), * 用最快的方式排序 * @param args */ public static void main(String[] args) { //例如:一个数组中有65535个数不重复的大

面试题3:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

package siweifasan_6_5; /** * @Description:在一个长度为n的数组里的所有数字都在0到n-1的范围内. * 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次. * 请找出数组中任意一个重复的数字. * 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2. * @Parameters: // Parameters: // numbers: an array of integers //

剑指offer(Java版)第一题:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。 *请找出数组中任意一个重复的数字。 *例如,如果输入长度为7的数组{2, 3, 1, 0, 2, 5, 3},那么对应的输出是重复的数字2或者3。

/*在一个长度为n的数组里的所有数字都在0到n-1的范围内. * 数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次. * 请找出数组中任意一个重复的数字. * 例如,如果输入长度为7的数组{2, 3, 1, 0, 2, 5, 3},那么对应的输出是重复的数字2或者3.*/ import java.util.*; public class Class1 { static class findRepeatedNumber{ public int findRepeatedN

位运算-异或运算-只出现一次的数字-给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 示例: 输入: [2,2,1] 输出: 1 输入: [4,1,2,1,2] 输出: 4 GO: func singleNumber(nums []int) int { var num int for i := 0; i < len(nums); i++ { num = num ^ nums[i]; } return num; } PHP: class Solution { /** * @par

61. 从1到n,共有n个数字,每个数字只出现一次。从中随机拿走一个数字x,请给出最快的方法,找到这个数字。如果随机拿走k(k&gt;=2)个数字呢?[find k missing numbers from 1 to n]

[本文链接] http://www.cnblogs.com/hellogiser/p/find-k-missing-numbers-from-1-to-n.html  [题目] 从1到n,共有n个数字(无序排列),每个数字只出现一次.现在随机拿走一个数字x,请给出最快的方法,找到这个数字.要求时间复杂度为O(n),空间复杂度为O(1).如果随机拿走k(k>=2)个数字呢? [分析] 题目给出的条件很强,数字是从1~n的数字,限制了数字的范围:每个数字只出现一次,限制了数字出现的次数:随即拿走了一