在一个整型数组中有两个数 a 、b值出现一次,其他数都出现两次。求出 a 和 b

来源:互联网 发布:nginx 别名配置 编辑:程序博客网 时间:2024/06/03 18:59

类似我的上一条博文,利用位运算求出数组中只出现一次的数

这道题很明显运用位运算来求解效率才是最高的。


但是出现了一个问题,现在只出现一次的数有两个,如果还是将所有数进行异或操作,

那么最终的结果是 a^b 。 但是我们现在要求的是 a 跟 b 分别为多少而不是 a^b.。那么要怎么做呢?


首先我们要明确,进行异或操作的做法是正确,那么怎样才能求出来呢?如果是只有一个数出现一次,

其他数出现两次那就很容易了但现在是两个数 只 出现一次。那么我们是不是可以把 这个数组分为两部分

A 部分 和 B 部分,并且使得 a 在 A 中而 b 在 B中,且其他数在 A 或者B中要出现两次,而不是一个在A

中一个在 B中。这样我们就可以分别对 A 、B中的元素进行 异或,得到的结果就是我们要求的 a 和 b 。

想法有了 ,but how to do ?如何将数组分成符合我们需求的 A 和 B呢?

我们是要从 a ^ b入手,先码具体代码:

/** * <p>给定一个整型数组,数组里面只有两个元素出现一次,其他元素都出现两次</p> * <p>很明显要运用位运算的知识</p> * @deprecated num1 和  num2 分别用于存储我们要求的那两个数 *  * @author luzi * */public class FindNumsAppearOnce {public void findNumsAppearOnce(int[] arr,int[] num1,int[] num2){int temp = 0;int indexOf1 = 1;for(int i = 0;i < arr.length;i++){temp^=arr[i];//将所有的元素进行异或运算,则最后的结果是两个不同的元素的异或结果}while((temp&1) == 0 && indexOf1 < 32){//这一步是关键,找出 temp 中第一个为 1 的位,据此将数组分为我们需要的A 和 BindexOf1++;temp=temp>>1;}temp = 1<<(indexOf1 - 1);for(int i = 0;i < arr.length;i++){if((arr[i]&temp) == 0)num1[0] ^= arr[i];elsenum2[0] ^= arr[i];}}}

关键的一步:

while((temp&1) == 0 && indexOf1 < 32){indexOf1++;temp=temp>>1;}
因为a 和 b 不同,那么它们异或的结果 temp 至少有一位为 1 ,我们现在要做的就是找出 temp 第一个 1 哪一位。

将 temp 和 1 进行与运算,如果 temp 第一位 为 1(当然是指temp 右边的第一位也就是 低位)那么 temp & 1 = 1,

如果不是 那么 temp & 1 = 0 。我们现在进行一个 while 循环,当 temp & 1 = 0 时证明temp 第一位不是 1 ,我们将

temp 进行右移,同时将 indexOf1++ (用于记录当前比较的在哪一位)。当找到 第一位 1 时 while 循环停止,我们现在

得到 第一位 为 1 的位置 indexOf1. 。得到这个有什么用?得到这个我们就能将 数组分为我们要的 A 和 B 了!我们知道,

进行异或操作后得到的 temp 出现的 1 对应的那一位,必定是 在 a 、b中的某一个对应位是 1,而不是两个对应位都是  1,

如果都是  1 (或者都是 0)那么异或后对应位的结果就是 0 了。比如 0010 ^ 1011 = 1001. ,我们得到第 indexOf1 = 0位为 1

此时我们可以用 1 作为 比较标准,与 1 异或结果为 1 的放进 A中,与 1 异或结果 为 0 放进 B中,此时 A 和 B就是我们要求的。

如果indexOf = k ,那么我们就需要以  1<<k-1 作为比较的标准了,为什么?这个其实很好理解,只有懂位运算就能理解了。

这里就不赘述了。接下来我们就进行实际的分类来分为 A 和 B:代码比较简洁

temp = 1<<(indexOf1 - 1);for(int i = 0;i < arr.length;i++){if((arr[i]&temp) == 0)num1[0] ^= arr[i];elsenum2[0] ^= arr[i];}
到此,终于解决问题了!总结一下这题:位运算,以及将问题细化。即上面分析中将 数组分为 两个不同部分 A 和 B这一个方法,

非常重要,需细细体会。




1 0