寻找缺失的数

一:题目

有一组数字,从1到n,中减少了一个数,顺序也被打乱,放在一个n-1的数组里

请找出丢失的数字,最好能有程序,最好算法比较快
BTW1: 有很多种方法的哦,据说O(n)的方法就不止一种
BTW2: 扩展问题,如果丢失了2个数字呢?
BTW3: 一定要小心不要溢出,嗯,面试者有时候不会提醒你的
BTW4: 最好不要多申请n多空间
Update 一个很相近的题目:1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?

题目 :
给你n个数,其中有且仅有一个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那一个数。

给你n个数,其中有且仅有两个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那两个数。

第一题: 看到题目的第一反应就是把这个n-1个数字加起来,然后和1+2+3..+n的和进行比较,那个差值就是迷失的数字。但是这个方法正是BTW3里面提到的不要溢出:)所以有一定的风险,还是不采用,还有人说用一个个+-来判断,可以是可以但是代码写起来也比较难看(个人感觉),还是异或操作符来的最合适一些。 我们知道1^1=0;2^2=0;n^n=0;k^0=k;所以如果我们把这n-1个数字异或起来,再来异或一下1,2,..n。那么最终的答案肯定是迷失数字k^(1^1)^(2^2)...^(n^n)。也就是K了。我们很容易地写下来了函数:

思路1:

  求出1-n的总和,减去数组中n-1个数,则剩下的值就是丢失的数字。这种方法求和的时候,有可能溢出。可以采用1-a[0]+2-a[1]+3-a[2]+....n-1-a[n-2]+n;

/*
*功能:1-n的和,与数组和的相差就是所缺少的数.这种方式只适合于数比较小,不然和会溢出.
*也有不溢出的方式:1-a[0]+2-a[1]+3-a[2]+....n-1-a[n-2]+n;
*/
int find_missing_num_add(int *arr,int n)
{
	int ret=n*(n+1)/2;
	for(int i=0;i<n-1;i++)
		ret-=arr[i];
	return ret;
}

思路2:

  采用异或。异或的性质:0^1=1,1^1=0,0^0=0。所以可以采用1^2^3....^n^a[0]^a[1]....^a[n-1]。因为相同的数异或结果为0,而0与n异或的结果为n。

// in case find one missing number, here size is 1 less than the range n
int find_missing_number1 (int a[], int size)
{
	int number=0;
	for (int i=0;i<size;i++)
		number ^= ((i+1)^a[i]);
	number ^= (size+1);
	return number;
}

第二个问题来的更复杂一些,如果有两个数字迷失怎么办?还是方法一的方法,但是需要衍生一下。假定我们迷失的数字是S1,S2那么我们全部异或之后得到的就是S1^S2只有的值。分析一下就可以知道,S1!=S2,也就是说S1^S2!=0; 这样也就是说S1^S2的这个值有二进制位有一位是1,那么我们就可以把这些所有的数字分成2组,一组这个二进制位是1,另一个这个二进制位是0的来重新做异或。这样就可以吧其中一个S1求出来了,那再S1^(S1^S2)一下,S2也就得到了。

思想:

   也是采用异或。

    假设,缺失的数为s1和s2。则s1^s2=1^2^3.....^n^a[0]^a[1]^....a[n-3]。这个式子一目了然,无需多解释。问题是如何通过这个式子求出s1与s2的值。只要能求出一个值,比如说s1,则s2=s1^(s1^s2)。

   s1^s2的值必然不为0,则必然存在一位,s1与s2在此对应位不同。我们就可以按照此对应位是0或者1,将1-n分为两堆,将a[0]-a[n-3]分为两堆。将该为为1的两堆数相异或就能求出缺失的一个数。

   举个例子。1-7中缺失3,4。转化为二进制位:011和100。三位都不同,我们用最后一位来判别,将1-n和数组非为两堆。

  则结果为:

  

标志位(最后一位)10
1-n1、3、5、72、4、6
a[0]-a[n-3]1、5、7 2、6
用标志位为1的数进行异或

1^3^5^7^1^5^7=3。这样就求出了一个缺失数。

类似地,我们写下了如下的代码:

// in case find two missing numbers, here size is 2 less than the range n
void find_missing_number2 (int a[], int size, int& miss1, int& miss2)
{
	miss1 = 0;
	miss2 = 0;
	int number=0;
	for (int i=0;i<size;i++)
		number ^= ((i+1)^a[i]);
	number ^= (size+1);
	number ^= (size+2);	
	
	// now number will be miss1^miss2
	// find the binary 1 in number
	int k = number - (number&(number-1));
	for (int i=0;i<size;i++)
	{
		if ( (i+1)&k )
			miss1 ^= (i+1);
		if ( a[i]&k )
			miss1 ^= a[i];
	}
	if ( (size+1) & k )
		miss1 ^= size+1;
	if ( (size+2) & k )
		miss1 ^= size+2;
	miss2 = number ^ miss1;
}

转自:

http://blog.csdn.net/lanyan822/article/details/7823440

http://blog.csdn.net/hhygcy/article/details/4581731


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值