一、问题描述
一个数组中只有两个数字是出现一次,
其他所有数字都出现了两次。
找出这两个只出现一次的数字,编程实现。
例如数组为{1,3,5,7,1,3,5,9},找出7和9 。
二、解决问题
1.异或运算知识
首先我们应该对异或运算有一定的了解,即 ^
符号。并且对其运算规律非常熟悉。
(1)异或运算中,是有关二进制的相互运算,规定对应权位两个数相同为0,相异为1。例如1^2则可以拆分为001 ^ 010 。001的第一个1对应010的第一个0,它们并不相同则运算结果为1,以此类推,最后算出的结果为011,转换为十进制则为3。
(2)而有两个个特点,一是任何一个数与0进行异或运算结果都为它本身。二是任何数与其本身进行异或运算结果都为0。
2.解决思路
(1)一个整型数组中,若只有两个数字只出现一次,其他数字都出现了两次,那么把数组中的所有遍历异或一遍的结果就是只出现一次的两个数字相异或的结果。
(2)在上面我们介绍到相同的两个数异或结果为0,则举一个例子,如一个整型数组{1,3,5,7,1,3,5,9},该数组只有7和9出现过一次,而其它数字都出现了两次。全部数字进行异或运算后计算的结果等同于7和9进行异或运算的结果。
(3)这道题最有趣的地方就在于如何把只出现一次的数字打印出来。思路是将7和9进行异或运算后的结果与1进行逻辑与运算观察该二进制最早在第几个位置出现了数字1。两个数相异或的结果中的“1”表示这两个数在该位上不同,而恰好在该位上不同可以将该数组分成两个子数组。即将{1,3,5,7,1,3,5,9}的数组分为{1,1,5,5,9}与{3,3,7}两个数组(自己可以去尝试把它们的二进制列出来就能分成以上两组)。再将以上两组分别再进行异或运算,即能成功分离出9与7两个数字,再进行打印则解决问题。
三、源代码
利用数组下标进行运算:
void Find(int arr[], int sz)
{
int num1 = 0;
int num2 = 0;
int ret = 0;
int i = 0;
int n = 0;
for (i = 0; i < sz; i++)
{
ret ^= arr[i]; //异或所有数
}
for (n = 0; n < 32; n++)
{
if
{
(((ret >> n) & 1 )== 1);//计算第几个数出现1,从0开始
}
break;
}
for (i = 0; i < sz; i++)
{
if (((arr[i] >> n) & 1) == 1) //为两个只出现1次的数字的不同位进行分组
{
num1 = num1 ^ arr[i]; //该位为1的数字全部进行异或
}
else
{
num2 = num2 ^ arr[i]; //该位为0的数字全部进行异或
}
}
printf("%d %d", num1, num2);
}
#include <stdio.h>
int main()
{
int arr[] = { 1, 2, 3, 3, 2, 1, 4, 5 };
int sz = sizeof(arr) / sizeof(arr[0]);
Find(arr,sz);
return 0;
}
利用指针进行运算:
#include <stdio.h>
void Find(int* arr, int sz)
{
int num1 = 0;
int num2 = 0;
int i = 0;
int ret = 0;
int n = 0;
for (i = 0; i < sz; i++)
{
ret = ret ^ (*(arr + i));//异或所有数
}
for (n = 0; n < 32; n++)
{
if (((ret >> n) & 1 )== 1)//计算第几个数出现1,从0开始
{
break;
}
}
for (i = 0; i < sz; i++)
{
if (((*arr+i) >> n & 1 )== 1)//为两个只出现1次的数字的不同位进行分组
{
num1 = num1 ^ (*(arr + i));//该位为1的数字全部进行异或
}
else
{
num2 = num2 ^ (*(arr + i));//该位为0的数字全部进行异或
}
}
printf("%d %d", num1, num2);
}
int main()
{
int arr[] = { 1, 2, 3, 3, 2, 1, 4, 5 };
int sz = sizeof(arr) / sizeof(arr[0]);
Find(arr, sz);
return 0;
}