找出丢失的数字 (XOR)

本文介绍了如何使用位操作XOR解决面试中常见的问题:找到丢失的数字,以及在一个数组中找到重复的数字。通过分析1到n的异或结果与输入数组的异或结果,可以高效地找出丢失的数字。此外,文章还探讨了其他可能的解决方案,如累加、乘积、排序和哈希,并讨论了它们的优缺点。最后,提出了在更大的数列中寻找重复元素的挑战及解决方案。

问题:

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

分析:

这类题是频率很高的面试题,这类题的主要思路还是用XOR位操作,利用XOR位操作的原理是如果两个相同的数进行XOR操作,结果是0,而0与数A进行位操作,结果仍旧是A。所以,我们可以用1^2^...^n的结果逐个异或当前输入数据,而最后的结果是丢失的那个数字。时间复杂度为O(n),空间复杂度O(1)。

我们也可以用其它的方法,但是,相比起位操作,其它方法都有所缺陷,比如:

1. 用1+2+...+n(即n(n+1)/2)减去当前输入数据的总和。时间复杂度为O(n),空间复杂度O(1),缺点是容易溢出。缓解溢出的方法,求1+2+...+n的时候,边加边减。假如数组为a,那么这可以这么计算1-a[0]+2-a[1]+...+(n-1)-a[n-2]+n。
2. 用1*2*...*n除以当前输入的数据的积。时间复杂度O(n),空间复杂度O(1)。缺点也是容易溢出,缓解的办法:1/a[0]*2/a[1]...(n-1)/a[n-2]*n(得用浮点除法)。
3. 对输入数据排序,然后从头到尾遍历一次。时间复杂度O(nlgn),空间复杂度O(1)(还取决于排序算法)。缺点很明显,时间复杂度过高。
4. 对输入数据进行Hash,然后从头到位遍历一次。时间复杂度O(n),空间复杂度O(n)。

如果是少了两个数,那又该怎么办呢?

我们可以用1^2^...^n的结果逐个异或当前输入数据,得到x^y。由于x与y不相等,所以x^y的结果中至少有一位是1。我们根据这一位的不同(0或1)将输入数据以及1,2,...,n分成两堆。然后,分别对两堆进行异或即可得到x和y。

问题扩展2:1-1000放到含有1001个元素的数组中,只有唯一的一个元素重复,其他均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来。要求不用辅助存储空间。
这个问题的解法类似,用1^2^...^1000的结果逐个异或输入的1001个元素,得到的结果即为所求。
相似问题:
给你n个数,其中有且仅有一个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那一个数。

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

此题代码来自 http://zhedahht.blog.163.com/blog/static/2541117420071128950682/

///////////////////////////////////////////////////////////////////////
// Find two numbers which only appear once in an array
// Input: data - an array contains two number appearing exactly once,
//               while others appearing exactly twice
//        length - the length of data
// Output: num1 - the first number appearing once in data
//         num2 - the second number appearing once in data
///////////////////////////////////////////////////////////////////////
void FindNumsAppearOnce(int data[], int length, int &num1, int &num2)
{
      if (length < 2)
            return;
 
      // get num1 ^ num2
      int resultExclusiveOR = 0;
      for (int i = 0; i < length; ++ i)
            resultExclusiveOR ^= data[i];
 
      // get index of the first bit, which is 1 in resultExclusiveOR
      unsigned int indexOf1 = FindFirstBitIs1(resultExclusiveOR);
 
      num1 = num2 = 0;
      for (int j = 0; j < length; ++ j)
      {
            // divide the numbers in data into two groups,
            // the indexOf1 bit of numbers in the first group is 1,
            // while in the second group is 0
            if(IsBit1(data[j], indexOf1))
                  num1 ^= data[j];
            else
                  num2 ^= data[j];
      }
}
 
///////////////////////////////////////////////////////////////////////
// Find the index of first bit which is 1 in num (assuming not 0)
///////////////////////////////////////////////////////////////////////
unsigned int FindFirstBitIs1(int num)
{
      int indexBit = 0;
      while (((num & 1) == 0) && (indexBit < 32))
      {
            num = num >> 1;
            ++ indexBit;
      }
 
      return indexBit;
}
 
///////////////////////////////////////////////////////////////////////
// Is the indexBit bit of num 1?
///////////////////////////////////////////////////////////////////////
bool IsBit1(int num, unsigned int indexBit)
{
      num = num >> indexBit;
 
      return (num & 1);
}

参考:http://www.cnblogs.com/youwang/archive/2011/03/30/find_missing_number.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值