提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
题目
一个数组中只有两个数字是出现一次,其他所有数字都出现了两次,编写一个函数找出这两个只出现一次的数字。
解题思路
前提条件
异或的特点就是两个相同的数字进行异或,得到的结果为0。任何数与0异或是其本身。
第一步
我们可以把所有的数字异或一遍,最终得到的就是两个单身狗数的异或结果。
假设 ar[] = { 1,2,3,4,5,1,2,3,4,6 },其中不同的是5,6,用temp存储。
第二步
为将众数字分成两份,只需要根据temp上任意1的位置就可以区分5,6,然后区分别的数字
定义一个mask,使mask取temp上的任意一位1
(找到其结果任意二进制为1(异或结果不同才会为1)的一位赋值给mask)
int mask = 0x01;
//0000 0001
//0000 0011 temp
while ((mask & temp) == 0)
mask <<= 1;
//0000 0001 mask & temp第一次!=0 跳出循环
第三步
将每个数字与mask 按位与 将数字分为两部分,然后就是要将两组数各自组内 异或 就可以得到两个单身狗数。
代码呈现
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
// 单身狗2
//一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。
//编写一个函数找出这两个只出现一次的数字。
//
//例如:
//
//有数组的元素是:1,2,3,4,5,1,2,3,4,6
//只有5和6只出现1次,要找出5和6.
//解题思路:
//异或的特点就是两个相同的数字进行异或,得到的结果为0
//任何数与0异或是其本身。
//我们可以把所有的数字异或一遍,最终得到的就是两个单身狗数的异或结果。
//假设 ar[] = { 1,2,3,4,5,1,2,3,4,6 },其中不同的是5,6,用temp存储。
//为将众数字分成两份,只需要根据temp上任意1的位置就可以区分5,6,然后区分别的数字
//将每个数字与mask ==按位与== 将数字分为两部分,然后就是要将两组数各自组内 ==异或== 就可以得到两个单身狗数。
int* FindNum2(int arr[], int n)
//不需要int* arr[],因为我们的目的自是找出数字就行,不需要对原数组进行修改,临时地址就够用
{
int temp = 0;
for (int i = 0; i < n; i++)
temp ^= arr[i];
//5 0101
//6 0110
//temp 0011
int mask = 1;
while ((mask & 1) == 0)
mask << 1;
//mask 0X0001
//temp 0X0011
//此时就已经mask & temp != 0
// 所以 mask 0X0001
int* res = (int*)calloc(2, sizeof(int));
//必须创建指针数组,不然临时变量等运行结束会消失
//通过动态内存的方式创建两个int大小内存空间
assert(res != NULL);
for (int i = 0; i < n; ++i)
{
if ((arr[i] & mask) != 0)
res[0] ^= arr[i];//res[0] = 0 ^ 1^ 5^ 1 = 5
else
res[1] ^= arr[i];//res[1] = 0 ^ 6 ^4 ^4 = 6
}
return res;
//因为这两个地址相连,直接传回去就行
}
int main()
{
int ar[] = { 1,2,3,4,5,1,2,3,4,6 };
int n = sizeof(ar) / sizeof(ar[0]);
int* res = FindNum2(ar, n);
//创建指针接收地址
int sz = 2;
for (int i = 0; i < sz; i++)
{
printf("%d ", res[i]);
}
return 0;
}