开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情
前言
本文是关于Leetcode刷题——寻找两个单身狗的C语言实现。
新手一个,水平比较低,还请包涵。
题目描述
一个整型数组nums中只有两个数字是出现一次,其他所有数字都出现了两次。 编写一个函数找出这两个只出现一次的数字。
示例 1:
输入:nums = [1,2,1,3,2,5] 输出:[3,5]
解释:[5, 3] 也是有效的答案。
示例 2:
输入:nums = [-1,0] 输出:[-1,0]
示例 3:
输入:nums = [0,1] 输出:[1,0]
提示:
除两个只出现一次的整数外,nums 中的其他数字都出现两次。
计数统计法
时间复杂度:O(n)
思路
创建一个动态数组buk,利用其下标对应原数组中的元素,利用buk中对应下标的值作为该下标对应的值出现的次数,类似于计数排序中的计数方法, 就是利用数组记录各数字出现次数。
代码
int* FindSingle(int* arr, int sz)
{
int max = 0;
int i = 0;
int j = 0;
int cnt = 0;
static int ret[2] = {0};
for (i = 0; i < sz; i++)//找出最大值以确定动态数组元素个数
{
if (arr[i] > max)
max = arr[i];
}
int* buk = (int*)calloc(max, sizeof(int));
if (NULL == buk)
{
perror("buk");
return;
}
for (i = 0; i < sz; i++)//计数并存储到数组buk中
{
buk[arr[i]]++;
}
for (j = 0; j < max; j++)//遍历以找出只出现一次的两个数字
{
if (buk[j] == 1 && cnt < 2)
{
ret[cnt] = j;
cnt++;
}
}
free(buk);
buk = NULL;
return ret;//将找到的两个数字存到全局数组中并返回其指针
}
int main() //以一个20个元素的数组作为示例
{
int arr[20] = {1, 2, 3, 3, 4, 4, 5, 6, 6, 5, 7, 9, 8, 7, 8, 9, 10, 10, 11, 11};
int* tmp = FindSingle(arr, 20);
printf("%d %d\n", tmp[0], tmp[1]);
return 0;
}
分组异或法
时间复杂度:O(n)
思路
要是只寻找一个单身狗,就可以无脑异或:遍历数组,把全部数异或在一起。为什么可以这样呢?是根据异或的两个小特点:一是相同的数异或后为0,二是任何数和0异或都得到它本身。基于此,在把全部数异或的时候相同的数就全部化为0了,而异或0对数值没有影响,最后就剩下单身狗了(悲)。
不过这里是要寻找两只单身狗,要是全部异或在一起最后得到两个单身狗异或的值,无法分离。那可不可以把两只单身狗分开呢,把他俩分到不同的组去,分开来异或,这样不就分别筛选出两只单身狗了吗。不过难就难在如何分组。
想想看,能不能利用一下两只单身狗异或的值,这里举个例子:数组arr[] = {1, 2, 3, 4, 4, 3, 2, 1, 5, 6};很明显5和6是单身狗。5和6异或得到的值的二进制位中看看最右边为1的是哪一位,在那一位上5和6的二进制位不同,可以以此来分组。分组以后再分别和组内的数异或即可。
步骤
1.全部数异或
2.分组: 根据两单身狗在二进制位第n位上不同来分组
3.分别异或
代码
void FindSingle(int* arr, int sz, int* sig_1, int* sig_2)
{
int i = 0;
int tmp = 0;
int pos = 0;
for (i = 0; i < sz; i++)
{
tmp ^= arr[i];
}
while (tmp)
{
if (tmp % 2)
break;
tmp /= 2;
pos++;
}
for (i = 0; i < sz; i++)
{
if ((arr[i] >> pos) & 1)
{
*sig_1 ^= arr[i];
}
else
{
*sig_2 ^= arr[i];
}
}
}