单身狗问题
题目:一个数组中只有两个数字是出现一次,其他所有数字都出现了两次,找出这两个只出现一次的数字。(要求:时间复杂度O(n))
- 法一:将数组中所有数进行排序,在这里选择较为简单的冒泡排序。数组有序之后,从数组下标0为开始,往后两两进行比较(如a[0]和a[1],代码可写为a[0]-a[1]),若比较结果为0,说明这两个数相同,则进行下两个数的比较(如a[2]和a[3]);若比较结果不为0,说明此时所比较的两个数不同,单身狗就为这两个数中"左边"的那个(下标靠近0的那个,如a[n]和a[n+1]),找到一个后开始找下一个,此时,我们需要跳过这个"单身狗",开始比较"单身狗"后面的两个数,以此类推。有些人可能会问了,那为什么单身狗不能是"右边"的那个呢?因为我们已经对数组进行过排序了,在排序好的情况下,"单身狗"一定是左边的那个,若是两个单身狗连续排在了一起,经过上面的步骤也可以找出来。
#include<stdio.h>
void Find1(int* a, int n, int* num)
{
int tmp = 0;
int i = 0;
//由小到大排序
for (i = 0; i < n-1; i++)
{
int j = 0;
for (j = 0; j < n - i-1; j++)
{
if (a[j] > a[j + 1])
{
tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
}
//排序结果为1 1 2 2 3 4 5 5 6 6
int sign = 0;
i = 0;
int k = 0;
while (i<n)
{
//两两相比较,若相减为0,说明大小相同。比较完后,比较下两个,i+=2;
//若相减不为0,则必定是下标为i(a[i]与a[i+1]比较,靠近起始下标0的那个)的数字是“单身狗”,找到之后就跳过这个“单身狗”,即i++
if (!(a[i] - a[i + 1]))
{
i += 2;
}
else
{
*(num + k) = a[i];
k++;
i++;
if (k == 2)
break;
}
}
}
int main(void)
{
int i = 0;
int arr[] = { 1,2,3,5,6,1,2,4,5,6 };
int num[2] = { 0 };//想要找出两个数,不能简单的retutn,把数组num的地址传过去,即可修改num数组内容,达到目的
int sz = sizeof(arr) / sizeof(arr[0]);
Find1(arr, sz, num);
for (i = 0; i < 2; i++)
{
printf("%d ", num[i]);
}
return 0;
}
- 法二:异或法
根据异或的规则,相同为0,不同为1。
如
5 0101
6 0110
异或 ^ 1011
即根据ret异或的结果可知,ret每个二进制为1的位置,所对应的两位数字在此位置不同,如从右往左数第1,2,4位上5和6形态位置所对应的二进制位皆不同。
#include<stdio.h>
//异或法
void Find2(int *a,int n, int* num)
{
int ret = 0;//记录异或结果
int i = 0;
for (i = 0; i < n; i++)
{
ret ^= a[i];
}
//得到的结果必定不为0
//如
//5 0101
//6 0110
//^ 1011 (相同为0,不同为1)
//即根据ret异或的结果可知,ret每个二进制为1的位置,所对应的两位数字在此位置不同,
//可据此把两位不同数组分成两拨,每一波全部异或,异或结果即为单身狗
int m = 0;
while (m < 32)
{
if (ret & (1 << m))
break;
else
m++;
}
int x1 = 0;
int x2 = 0;
for (i = 0; i < n; i++)
{
if (a[i] & (1 << m))
{
(*num) ^= a[i];
}
else
{
(*(num+1)) ^= a[i];
}
}
}
int main(void)
{
int i = 0;
int arr[] = { 1,2,3,5,6,1,2,4,5,6 };
int num[2] = { 0 };//想要找出两个数,不能简单的retutn,把数组num的地址传过去,即可修改num数组内容,达到目的
int sz = sizeof(arr) / sizeof(arr[0]);
Find2(arr, sz, num);
for (i = 0; i < 2; i++)
{
printf("%d ", num[i]);
}
return 0;
}
- 法一的时间复杂度为O(N^2),法二的时间复杂度为O(N)
相比较而言,异或法的效率更加高效。
如有错误,或有其他方法,欢迎各位前来指正。