单身狗版本1
1.题目描述
一个数组中只有一个数字出现一次,其余数字均是成对出现,请找出只出现一次的数字。例如:{1,1,2,2,3,3,4,4,5},那么这个数字就是5。
2.解题思路
我们采用异或法解决,首先我们先来看异或的定义:异或指的是二进制中,对应的二进制位相同时异或为零,相异时异或为一,且异或还具有交换律。
- a^a=0 (和自身异或值为0)
- a^0=a (和0异或值为自身)
- a^b^a=a^a^b (异或满足交换律)
异或具有如上性质,那么单身狗问题就是数组中的值自身异或,成对出现的异或为0,最后0和只出现一次的数字异或,得到数字本身。最终得到的值就是只出现过一次的值。
3.代码实现
int find_single_dog1(int arr[], int sz)
{
int ret = 0;
int i = 0;
for (i = 0; i < sz; i++)
{
ret ^= arr[i];
}
return ret;
}
int main()
{
int arr[] = { 1,1,2,3,4,4,3,5,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
int dog = find_single_dog1(arr, sz);
printf("%d\n", dog);
return 0;
}
单身狗版本2
1.题目描述
一个数组中只有两个数字是单独出现一次,其余数字均是成对出现,请找出这两个只出现了一次的数字。例如:数组元素为:1,1,2,2,3,3,4,4,5,6.那么这两个数字就是5和6.
2.解题思路
我们还是使用异或法,但我们需要思考一个问题,两个数字的话我们怎样进行异或?我们这样思考,当把数组中所有的数字异或时,得到的结果是两个只出现一次数字的异或,以上面的数组为例,异或得到的结果就是5^6,那我们将原始数组进行分组,把5和6分开,再让两个分开后的数组自己异或,就能得到结果。思路总结如下:
- 分成两个组,每个组都有一个单独出现的数字。
- 相同的数字必须在同一个组。
以上面数组为例,5的二进制位是101,6的二进制位是110,我们可以按照第一位0或1进行分类,第一位为1的数组可分为1 1 3 3 5,第一位为0的数组可分为2 2 4 4 6,于是我们就完成了分类,假如第一位是相同的,那么我们就可以找第二位0或1进行分类。
再来学习一个运算符:右移运算符(>>)定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
3.代码实现
这段代码函数核心是3个功能,第一个功能找到原本数组异或后的结果,把这个结果保存下来,以上面数组为例:异或相异为1,那么上面101^110就是011。第二个功能找到结果中两个数不同的位,并把它用pos变量记录下来,011>>0再&1,结果就是1,所以pos为1。第三个功能根据位数上不同的0和1进行分类。最后把得到的dog1和ret再异或,就得到了dog2,如:5^5^6=6。
void find_single_dog2(int arr[], int sz,int* pd1,int* pd2)
{
int ret = 0;
int i = 0;
for (i = 0; i < sz; i++)
{
ret ^= arr[i];
}
int pos = 0;
for (i = 0; i < sz; i++)
{
if (((ret >> i) & 1) == 1)
{
pos = i;
break;
}
}
for (i = 0; i < sz; i++)
{
if (((arr[i] >> pos) & 1) == 1)
{
*pd1 ^= arr[i];
}
}
*pd2 = ret ^ *pd1;
}
int main()
{
int dog1 = 0;
int dog2 = 0;
int arr[] = { 1,1,2,2,3,3,4,4,5,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
find_single_dog2(arr, sz,&dog1,&dog2);
printf("%d %d\n", dog1,dog2);
return 0;
}