找出单身狗问题

单身狗问题

题目:一个数组中只有两个数字是出现一次,其他所有数字都出现了两次,找出这两个只出现一次的数字。(要求:时间复杂度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)
    相比较而言,异或法的效率更加高效。

如有错误,或有其他方法,欢迎各位前来指正。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值