今天要和大家分享的是我最近做到的一个比较有趣的编程问题,希望对大家有所裨益,话不多说下面开始进入正题。
简单版本题目及解决
给定一个只包含整数的数组 nums
,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。
这是今天要讲解的问题的简化版本,也就是使用在一堆出现两次的数中找到那个只出现一次的数字,是不是有种在一堆情侣中找到一条单身狗的感觉。
在正式讲解题目之前我们先来了解一个单目操作符——^,异或操作。这个操作符的作用是对两个数字的二进制下的每一位进行操作,如果这一位两个数字相同(都是1或者都是0),那么结果的这一位就是0,如果这一位两个数不同(一个是1,另一个是0),那么结果的这一位就是1。
如图所示,是异或操作的结果,a和b上每一位相同的,到结果上就变为0,反之就变为1。
那么我们就能理解一件事,如果两个相同的数异或,那么结果一定为0,因为两个相同的数的每一位必定相同。
我们还可以得出一个结论,0和任何数字异或,得到的结果还是这个数字,因为0的每一位都是0,这个数字上的某一位如果是零,和0异或之后还是0,如果是1,异或之后还是1.
那么我们就可以轻易地得到这道题的解法,把数组中的数字全部异或,得到的结果就是我们想要的结果。
#include<stdio.h>
//找到唯一的数
int singleNonDuplicate(int* parr,int nums)
{
int ret = 0;
int i = 0;
//对数组中每一个数字进行异或操作
for (i = 0;i < nums;i++)
{
ret = ret ^ *(parr + i);
}
//输出异或结果
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,4,3,2,1,6,6 };
int i = 0;
int number1 = sizeof(arr) / sizeof(arr[0]);
for (i = 0;i < number1;i++)
{
printf("%d ", arr[i]);
}
printf("\n");
printf("%d", singleNonDuplicate(arr, number1));
return 0;
}
加深难度
那么我们对第一个问题加深难度,如果我们想要找到一个数组中的两个只出现一次的数字怎么办呢?
将所有数组异或的操作显然是不可取的,这只会得到一个两个只出现一次数字相互异或之后的结果。
我们需要把问题拆解,把一个数组分为两个数组,两个数组中分别包括两个只出现一次的数字,接下来睁大眼睛看是怎么操作的。
拆分数组
首先我们需要将所有数字异或,最后得到的结果是两个只出现一次数字的异或结果,这时结果二进制位中为1的位,就是两个数字的不同的地方,也就是我们可以以此为依据划分,保证我们想要的两个结果不会被分到一起。
异或求解
然后,就可以像解第一道题一样解决第二题了。
附上代码
#include<stdio.h>
//找到唯二的数
void singleNonDuplicate(int* parr,int nums,int* pc)
{
int temp = 0;
int ret[2] = {0,0};
int i = 0;
int j = 0;
//对数组中每一个数字进行异或操作
for (i = 0;i < nums;i++)
{
temp = temp ^ *(parr + i);
}
//获取第i位为1
for (i = 0;i < 32;i++)
{
if (1&(temp >> i))
{
break;
}
}
//分组异或
for (j = 0;j < nums;j++)
{
if (1 & (parr[j] >> i))
ret[0] = ret[0] ^ parr[j];
else
ret[1] = ret[1] ^ parr[j];
}
*pc = ret[0];
*(pc + 1) = ret[1];//输出异或结果
}
int main()
{
int arr[] = { 1,2,3,4,5,4,3,2,1,6,6,8};
int i = 0;
int number1 = sizeof(arr) / sizeof(arr[0]);
int ret[2] = { 0,0 };
singleNonDuplicate(arr, number1, ret);
for (i = 0;i < number1;i++)
{
printf("%d ", arr[i]);
}
printf("\n");
printf("%d ", *ret);
printf("%d", *(ret +1));
return 0;
}