首先来看看这个题目:
一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。
编写一个函数找出这两个只出现一次的数字。
例如:
有数组的元素是:1,2,3,4,5,1,2,3,4,6
只有5和6只出现1次,要找出5和6.
分析
首先来分析一下题目。发现除了5,6这两个只出现过一次的数字,其它数字都出现了两次。那也就意味着我们可以使用异或的方法来解决。
什么是异或?异或是位运算里面的一种。比较常见的位运算符有&(按位与),|(按位或),^(按位异或)等等。&的运算规则是有0则0,两个为1才是1;|的运算规则是两个为0才是0;^的运算规则是相同为0,相异为1。而因此可以得出,两个相同的数字进行异或运算之后就可以得出0。因此可以考虑将数组里面的元素用异或运算来找出两个只出现过一次的元素。
思路
首先让数组里面的所有元素都进行一次异或运算,最后得到的一定是两个只出现过一次的元素所进行异或运算的值。就本题来说,这个数组里面所有的元素异或运算之后,得到的就是5^6=3(二进制位为:0011)然后,我们可以根据5^6所得到的二进制位0011的任意一个"1"位来把这两个只出现过一次的元素给找出。为什么说找到二进制位上的某个是"1"的位就可以说找到两个只出现过一次的数字呢?因为从位运算的角度来说,当进行异或运算的时候,之有在”1“的位上,才能找到那两个只出现过一次的元素的不同点,用这个点来把这两个元素分开,如果说这两个数字的二进制位都是一样的话,那就没办法把这两个元素给分开了。
通过上面的操作,是把两个只出现过一次的元素给分开了,那怎么把这两个元素拿出并打印出来呢?我们可以考虑把最后一位都是"1"和最后一位都是"0"的元素分别放在一个数组里面,然后再各自进行异或运算,这样,相同的元素就会消失,只剩下最后的两个只出现过一次的数据。
请看代码:
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
int temp = 0;
int i = 0;
int num = 0;
for (i = 0; i < 10; i++)
{
temp ^= arr[i];//这里是把数组内的所有元素进行异或运算
//最终得出那两个只出现过一次的元素
}
for (i = 0; i < 32; i++)
{
if (temp & 1 << i)//这句话就是把上面异或运算得到的值
//进行向左移动i位以此来找到"1",如果找到位数是"1",就进入赋值语句
{
num = i;//把所需要移动的位数i赋给num
break;
}
}
//在上面,已经找到了值所需要向左移动的位数i
int a = 0;
int b = 0;
for (i = 0; i < 10; i++)
{
//相应的,在这里,通过向左移动i位来把以0/1为一句分开变成两个数组
if (arr[i] & 1 << num)//用数组里面的元素来进行向左移动i位,来找到1
{
a ^= arr[i];//找到1之后,把这个数组里面的所有位数都进行一遍异或运算
}
else//用数组里面的元素来进行向左移动i位,来找到0
{
b ^= arr[i];//找到0之后,把这个数组里面的所有位数都进行一遍异或运算
}
}
printf("%d %d", a, b);
return 0;
}
输出的结果为5 6
注意,在找到需要向左移动的位数i找到”1“之后,上面的两行相应的就需要移动i位,以此来找到”1“,”0“。