一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。 找出这两个数字,编程实现。

有类似的另外一个题目是:

        一个数组中出了一个单独出现的数,其他都是成对出现的,要求找出这个单独出现的数。解决办法是异或每一个数,结果就是这个单独出现的数。

#include<stdio.h>
int main()
{
     int arr[7]={1,2,3,9,3,1,2};
     int i=0;
     int size=sizeof(arr)/sizeof(arr[0]);
     int res=arr[0];
     for(i=0;i<size;i++)
     {
           res^=arr[i];
     }
     print("%d\n",res);
     return 0;
}

那么这道题也一样,只不过异或的结果是这两个单独出现的数异或的结果,需要做的就是将它们分开就行了。

原理:

        一个数异或自身,结果是0

        0异或任何数,结果为这个数

        Step1:异或整个数组,得出单独出现的两个数异或的结果。

                    我们可以得出结论——这个结果中为 1 的比特位表示这两个数的二进制序列在这个比特位不同。

              比如:0110
                   1010    ^
              -----------------
                   1100       //不看其他位,至少这两个数在第3个比特位是不同的

        Step2:从这个结果中将单独出现的两个数分类出来。

                    分类的原理就是Step1的结论,

                    我们需要在结果中查找第一个为1的比特位,需要的是它在32个比特位中所处的位置,假设为N。

                    可以断言这两个数至少在这个比特位是不同的。


                    接下来用1左移N位与每个数按位与,结果为1分为一组,否则分为另一组。

                    分别对每一组使用文首的方法的就可以得出这两个数。


        但是!我们的目的不是简单地将它做出来!我们要通过这道题在之前的基础上进一步了解异或,并且要写出

简单明了高效不臃肿的程序,要学会巧妙地优化程序,仔细看程序~。


#include<stdio.h>
int main()
{
     int arr[]={1,2,3,4,67,3,4,1,666,2};
     int size=sizeof(arr)/sizeof(arr[0]);
     int i=0;

     //求单独出现的两个数异或的结果
     int res=arr[0];
     for(i=0;i<size;i++){
         res^=arr[i];
     }
     
     //在res的二进制序列由低到高找出第一个为1的比特位
     //正常情况下,我们是用1位移来进行遍历二进制序列,之后需要另一个变量来接受i(位置)
     //那么像下面这样,用一个值为1的变量来查找岂不是很优秀?
     int flag=1;
     for(i=0;i<32;i++){
          if(res&(flag<<i)){
               break;
          }
     }
     
     //在分类的同时异或,大大提高了程序的效率。
     //一般情况下,我们首先想到的是将结果分类到两个数组中,然后分别进行分类
     //再进一步思考,分类的同时进行异或
     //下面这样岂不是更好
     int x=0;
     int y=0;
     for(i=0;i<size;i++){
          if(1==arr[i]&flag){
               x^=arr[i]
          }
          else{
              y^=arr[i];
          }
     }
     printf("%d,%d\n",x,y);

     return 0;
}
谢谢~

猜你喜欢

转载自blog.csdn.net/IronMan240/article/details/80349359
今日推荐