面试题1:落单的数

来源:互联网 发布:如何预防网络成瘾 编辑:程序博客网 时间:2024/04/28 02:27

题目来源:微信公众平台九章算法。由于九章算法只给出了算法,并没有给出源代码,这里笔者根据自己的能力给出了源代码,当然可能会有很多不足,希望大家不吝指教。                                             -——— 肖然

题目描述:

有2n+1个数,其中2n个数两两成对,1个数落单,找出这个数。要求O(n)的时间复杂度,O(1)的空间复杂度。进阶问题:如果有2n+2个数,其中有2个数落单,该怎么办?

题目分析:

异或的性质,对于任意a,a^a=0; a^0=a;根据这两条性质可以写出下列代码。

初阶:将2n+1个数异或起来,相同的数会抵消,异或的答案就是要找的数。


进阶:假设两个不同的数是a和b,并且a!=b,将2n+2个数异或起来就会得到c=a xor b,并且c不等于0。因此在c的二进制位中找到一个为1的位,可推断在这位上a和b分别为0和1,因此将2n+2个数分为该位位0的组和该位为1的组,两组中各自会包含2n’+1个数和2n’’+1个数,用初阶的算法即可解决。


源代码:
/**
 *九章算法面试题一、落单的数
 *异或的简单应用
 *异或的性质: a^a=0; a^0=a;
 */
#include<iostream>
#include<cstdio>
using namespace std;
int a[10000],b[10000];
int ans,ans0,ans1;
int LuoDan1(int n,int *a){//初阶问题,给出2*n+1个数,只有一个是单个的,求这个数
    ans=0;
    for(int i=0;i<2*n+1;i++){
        ans^=a[i];
    }
    return ans;
}
int Judge(int n,int k){//判断第k位(从右到左)是否为1
    int i=1;
    while(i++<k-1) n>>=1;//向左移位k-1位
    if(n>>1==1) return 1;//第k位是1,返回1
    else return 0;
}

int LuoDan2(int n,int *a){//初阶问题,给出2*n+2个数,只有两个是单个的,求这两个数
    ans=0;
    for(int i=0;i<2*n+2;i++){
        ans^=a[i];
    }
    int k=1;
    //cout<<ans<<endl;
    while(ans>0){//找到最低位的1
        if((ans&1)==1) break;
        ++k;
        ans>>=1;
    }
    ans0=ans1=0;
    for(int i=0;i<2*n+2;i++){
        if(Judge(a[i],k)){//第k位为1
            ans1^=a[i];
        }
        else ans0^=a[i];
    }
    //cout<<ans0<<" "<<ans1<<endl;
    return ans;
}
int main()
{
    int n;
    while(cin>>n){
        int ans=0;
        for(int i=0;i<2*n+1;i++){
            cin>>a[i];
            //ans^=a[i];
        }
        cout<<LuoDan1(n,a)<<endl;
        for(int i=0;i<2*n+2;i++){
            cin>>b[i];
        }
        //cout<<ans<<endl;
        LuoDan2(n,b);
        cout<<ans0<<" "<<ans1<<endl;
    }
    return 0;
}




1 0