一个百度的面试题目

问题:给一个很大的数组,里面有两个数只出现过一次,其他数都出现过两次,把这两个数找出来

 

原理:两个相同的数进行异或,其结果为0,两个不相同的数急性异或,其结果不为0。

举例:99 ^ 99 = 1100011b ^ 1100011b = 0

      95 ^ 99 = 1011111b ^ 1100011b = 0111100b= 60

 

在题目给出的很大的数组中,除要找的两个不同的数字外,其它的数字都是成对出现的,根据上面说到的两个相同的数字的异或其结果为0,因此,如果将整个数组中的元素进行异或,所得到的结果应该是所求的那两个不成对的数字的异或结果。假定数组中两个不同的数字是95 ^ 99,其异或的二进制结果是0111100b,其中有4位是1,这表明这两个数字的二进制有4位是不同的。(从右往左数) 它们分别是第3、4、5、6这4位,于是我们

只需要将数组中所有元素中第6位为1的元素和0111100b异或,

或者,将数组中所有元素中第5位为1的元素和0111100b异或,

或者,将数组中所有元素中第4位为1的元素和0111100b异或,

或者,将数组中所有元素中第3位为1的元素和0111100b异或。

就可以得到所求之两个数字中的一个数字,不妨以上面最后一条规则为例来进行说明:数组元素中第3位为1的数字,除所求的两个数字之外,都是成对出现的,它们所产生异或的结果肯定是0。而所求的那两个数字当中只有一个数字的第3位1,不妨假定这个数字是a(此时未知),另外一个要求的数字是b(此时未知),很明显,将a (此时未知)和0111100b异或就可以得到b (此时已知),再用0111100b和已经求出来的b (此时已知) 进行异或就可以得到a (此时已知)。比如上面的95的第3位为1,所以用95 ^ 0111100b = 1011111b ^  0111100b = 1100011b = 99,再用99 ^ 0111100b = 1100011b ^ 0111100b = 1011111b = 95。

 

有了上面的解题思路,代码就好写了。参考代码如下:

#include <iostream>
using namespace std;

void get_2_numbers(int* arr, int size, int& number1, int& number2)
{
	// 异或结果从右往左数第一个为1的bit的位置
	int pos = 0;

	// 异或结果
	int exclusive_or = 0;

	// 求出数组中各元素的异或
	for(int i = 0; i < size; ++i)
	{
		exclusive_or ^= *(arr + i);
	}

	number1 = exclusive_or;			// 临时保存异或结果
	/*
	while((exclusive_or & 1) == 0)	// 如果最右一bit是0,则循环
	{
		++pos;
		exclusive_or = exclusive_or >> 1;
	}
	// 循环结束时,得到的pos就是,异或结果中,从右往左数,第一个为1的bit的位置
	*/
	pos = exclusive_or & (-exclusive_or);   // 根据kingbigeast的建议,用这一行代码代替上面的while循环

	exclusive_or = number1;
	for(int i = 0; i < size; ++i)
	{
		if((*(arr + i) >> pos) & 1)
		{
			number1 ^= *(arr + i); 
		}
	}

	number2 = number1 ^ exclusive_or;

	return;
}

int main(int argc, char**)
{
	// 要求的两个数字
	int a, b;

	// 下面数组模拟大数组中,有两个不同的元素95、99,其它元素皆成对出现
	int arr[12] = {1,2,3,4,5,95,99,5,4,3,2,1};
	get_2_numbers(arr, sizeof(arr) / sizeof(int), a, b);

	cout << "所求的两个数字是:" << endl;
	cout << a << "\t" << b << endl;

	return 0;
}


  • 26
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 23
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值