题目链接地址:
题目描述:
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
输入:
每个测试案例包括两行:
第一行包含一个整数n,表示数组大小。2<=n <= 10^6。
第二行包含n个整数,表示数组元素,元素均为int。
输出:
对应每个测试案例,输出数组中只出现一次的两个数。输出的数字从小到大的顺序。
样例输入:
8
2 4 3 6 3 2 5 5
样例输出:
4 6
解题思路:
之前做过“数组中只有一个数字出现一次,而其他的数字都出现过两次”这个题目,那道题用的是异或运算。原理如下:
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
a ^ a = 0
也就是说两个相同元素异或得到的结果是0。
但是这道题升级了,变成了“数组中有两个数字出现一次,而其他的数字都出现过两次”,这下我没辙了。。。
于是请教范神,毕竟是玩数学的大神,立马就给了我一个思路:虽然出现一次的数字变成了两个,但是其他的数字都出现过两次,所以还是要用到异或运算。现在关键的问题是如何把这两个只出现一次的数字分开。
举个栗子,对于测试用例2 4 3 6 3 2 5 5 进行异或运算可以得到
2 ^ 4 ^ 3 ^ 6 ^ 3 ^ 2 ^ 5 ^ 5 = 4 ^ 6 = 2,
接下来就是要根据异或结果2分解出4和6,
将4,6,2换成二进制,具体如下:
4 ---- > 1 0 0
6 ---- > 1 1 0
2 ---- > 0 1 0
从右至左遍历数字’2’的二进制,可以得知第2个二进制位是1,
而异或运算满足:1 ^ 1 = 0,0 ^ 0 = 0,0 ^ 1 = 1,1 ^ 0 = 1;
所以可以得知数字4和6的第2个二进制位第一次出现了不同,其中4的第2个二进制位是0,6的第2个二进制位是1。
通过这个特点,我们就可以将数组中的数字分为两类:
①从左至右遍历整数所对应的二进制数,数字的第2个二进制位是0;
②从左至右遍历整数所对应的二进制数,数字的第2个二进制位是1;
属于第①类的数字与4进行异或,属于第②类的数字与6进行异或。
因此可以原来的数组就分为以下两组:
{4,5,5} 和{2,2,3,3,6},对这两组数字分为进行异或运算就可以得到数组中只出现一次的数字4和6。
AC代码如下:
#include<stdio.h>
#define MAX 1000001
int number[MAX];
/**
* 输入含有n个元素的整形数组
* @param n 表示数组的元素个数
* @return void
*/
void inputNumberArray(int n)
{
int i;
for(i = 0;i < n;i++)
scanf("%d",&number[i]);
}
/**
* 找到两个数字对应二进制中的最低不同位
* @param exclusiveOr 数组中所有元素异或得到的结果,因为除了两个元素之外,
* 其余元素都出现了两次,所以这个异或结果等价于两个只
* 出现一次的数字异或所得的结果
* @return int 两个只出现一次的数字的二进制最低不同位所对应的十进制数
*/
int getDifferentBit(int exclusiveOr)
{
int differentBit = 1;
while(0 == (exclusiveOr & 1))
{
differentBit = differentBit << 1;
exclusiveOr = exclusiveOr >> 1;
}
return differentBit;
}
/**
* 找到数组中两个只出现一次的数字
* @param n 表示数组的元素个数
* @return void
*/
void findTwoNumberOnlyOnce(int n)
{
int exclusiveOr = 0; // 保存异或运算所得到的结果
int differenceBit;
int i;
int a = 0,b = 0;
int temp;
for(i = 0;i < n;i++)
{
exclusiveOr = exclusiveOr ^ number[i];
}
differenceBit = getDifferentBit(exclusiveOr);
// 根据number[i]的第log2differenceBit位的数字是否为1,可以将数组中的元素分为两类:
// 一类数字的二进制与a的第log2differenceBit位相同,这类数字与a进行异或;
// 另一类的数字的二进制则与b的第log2differenceBit位相同,这类数字与b进行异或。
// 而与a同类的数字都各自出现了两次,这些数字两两异或后结果为0,最后的异或结果中只剩下a,同理可以得到b。
for(i = 0;i < n;i++)
{
if(0 != (number[i] & differenceBit))
{
a = a ^ number[i];
}
else
{
b = b ^ number[i];
}
}
// 如果a > b,则将a与b进行交换,保证最后的结果是a < b。
if(a > b)
{
temp = a;
a = b;
b = temp;
}
printf("%d %d\n",a,b);
}
int main()
{
int n;
while(EOF != scanf("%d",&n))
{
inputNumberArray(n);
findTwoNumberOnlyOnce(n);
}
return 0;
}
/**************************************************************
Problem: 1351
User: blueshell
Language: C++
Result: Accepted
Time:780 ms
Memory:4928 kb
****************************************************************/