问题描述
在一个一维数组中,有且只有两个不同的数单独存在,其他的数均成对存在。试设计程序找出两个单数(单身狗)。
问题分析
由于数组中只有两个数不是成对存在,其他的都是成对出现,那么只需将成对的数化整为零,剔除剩下的就是两个单数。
算法实现
方法一
将数组的元素进行逐个对比,找出两个单数。时间复杂度为O(N^2)。
void single(int* arr, int sz)
{
int tmp=0,j=0,num=sz-1;
for (int i = 0; i < sz; i++)
{
for (j = i+1;j<sz;j++)
{
tmp = arr[i];
if (tmp == arr[j])
{
arr[j] = arr[i+1];
arr[i+1] = tmp;
i++;
break;
}
else
{
if (j == sz-1)
{
arr[i] = arr[num];
arr[num] = tmp;
num--;
}
}
}
if (i >=sz - 2)
{
printf("%d ", arr[i]);
}
}
}
int main()
{
int arr[] = {1,2,34,5,5,2,6,1,34,11,23,11};
int len = sizeof(arr) / sizeof(arr[0]);
single(arr, len);
system("pause");
return 0;
}
方法二
运用二进制逻辑运算符进行查找。由于数组中除两个单数外的数都是成对存在,那么将这些数按位异或(^),相同为0,不同为1。异或之后,只剩下两个单数异或的结果。
两个单数不相同,则可以按某个二级制位为1,遍历整个数组,将这两个单数分离到两个不同的数组(因为异或相同为0,不同为1,这两个单数不相同,异或之后必定在二进制位上存在1)。再故技重施,将分离的两个数组异或,最后得到单数。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int num1 = 0, num2 = 0, pos = 0;
int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0,ret=0;
for (i = 0; i < sz; i++)
{
ret ^= arr[i]; //按位异或的规则是相同为0,不同为1;
//由于只存在两个不同的数,则异或的结果实质为这个两个不同数的异或
}
for (i = 0; i < 32; i++)//int类型占32位
{
if (((ret >> i) & 1) == 1)//右移找到1的位置
break;
}
pos = i;
for (i = 0; i < sz; i++)
{
if (((arr[i] >> pos) & 1) == 1)//按1的位置对数组分组,分两组使两个不同数分开
num1 ^= arr[i]; //再次按位与找到单数
}
num2 = ret ^ num1;
printf("%d %d \n", num1, num2);
system("pause");
return 0;
}
总结
寻找单数,要仔细分析数组的特点,如果采用暴力查找的办法,在数组很大的情况下,时间复杂度过高,导致程序执行效率低下。而采用异或的方法,大大提高了程序的执行效率。