找出数组中有3个出现一次的数字

题目:

一个int数组中有三个数字a、b、c只出现一次,其他数字都出现了两次。请找出三个只出现一次的数字。

下面要来看下如果找出这个与另外两个数的该bit位不同的数。

先看第一种情况,如果a,b,c三个数中,有两个该bit位为0,另一个为1,我们遍历数组,分别统计该数组元素中该bit位为1和0的元素个数,分别设为count1和count0,并同时将所有该bit位为1的元素异或,所有该bit位为0的元素异或,得到的结果分别设为temp1和temp0。如果count1为奇数,则可能有两种情况,a,b,c三个数的该bit位全为1,或者有两个为0,一个为1,如果有temp0==0,则说明是前一种情况(a,b,c的该bit位全为1的话,所有该bit位为0的每个元素出现了两次,因此异或后的结果为0),即3个只出现一次的数均在第1类中,此时没法找出其中的一个数,则直接跳到下次循环,继续判断下一个bit位,如果temp0不等于0,则说明是后一种情况(说明该比bit位为0的元素异或后没有完全抵消,则说明在此类中有两个数字只出现一次),此时其中一个只出现一次的数字就在另一类中,值就是temp1(重复的元素异或后都抵消了)。同理:第二种情况也是类似的,如果 count0为奇数时,且temp1!=0时,则说明有两个值出现了一次的数在bit=1的这类中,另一个则在bit=0的那一类中,且值为temp0;

/*
一个int数组中有三个数字a、b、c只出现一次,其他数字都出现了两次。请找出三个只出现一次的数字。
*/

/*
思路:按照上面提供的思路,现在3个次数值出现一次的数字中找出其中一个,然后利用上篇博文中找出两个在数组只出现一次的数字的方法。
*/

#include<stdio.h>
#include<stdlib.h>
int xorArr(int *arr,int len){
    if(arr!=NULL&&len>0){
        int result=arr[0];
        for(int i=1;i<len;i++){
            result^=arr[i];
        }
        return result;
    }
}
/* 
返回a的最低位的1,其他各位都为0 
*/
int findFirstBit1Index(int a){
    return a&(-a);// 
}
/* 
判断a中特定的位是否为1,若特定的位为 1,则返回true; 
这里的要判断的特定的位由b确定, 
b中只有一位为1,其他位均为0,由FindFirstBit1函数返回, 
而a中要判断的位便是b中这唯一的1所在的位是否为1 
*/ 
bool isBit1(int a,int b){
    return a&b;
}
//函数功能:找出数组中只有两个只出现一次的数字。
void findTwoNumInArrayOnlyOnce(int *arr,int len,int *first,int *second){
    if(arr==NULL||len<2){
        return;
    }
    *first=0;
    *second=0;
    //第一步:将arr中所有的数异或。
    int xorResult=xorArr(arr,len); 
    //第二步:找到异或结果中最右边为1的下标。 
    int index=findFirstBit1Index(xorResult); 
    //第三步:根据index将arr分成两个子数组,每个子数组中只有一个数字的次数为1,其余都为两次 
    for(int i=0;i<len;i++){
        if(isBit1(arr[i],index)){//此子数组中:为1 
            *first^=arr[i];
        }
        else{
            *second^=arr[i];
        }
    } 
}
//函数功能:检测数字a在第i为是否为  1 
bool isBit1In_i(int a,int i){
    return a&(1<<i); //将1左移 i在进行与 
}
//函数功能:检测数字a是否为奇数。 
bool isOdd(int a){
    return a%2;
} 
void findThreeNumInArrayOnlyOnce(int *arr,int len,int *first,int *second,int *third){
    if(arr==NULL||len<3){
        return;
    } 
    *first=0;
    *second=0;
    *third=0; 
    //将全部数据为成两类,第一类bit=1,第二类bit=0
    int countBit=sizeof(int)*8;//每个int类型的整数所占的bit数
    int count1,count0;//分别用来保存第一类和第零类中的元素个数。 
    int temp1,temp0;//分别用来保存第一类和第零类中所有元素的异或结果。 
    for(int i=0;i<countBit;i++){
        count1=count0=temp1=temp0;//每次循环时清零 
        for(int j=0;j<len;j++){
            if(isBit1In_i(arr[j],i)){//检测arr[j]在第i为是否为1. 
                count1++;
                temp1^=arr[j];
            }
            else{
                count0++;
                temp0^=arr[j];
            }
        }
        if(isOdd(count1)) {//count1为奇数时 即出现第一种情况:有两个出现一次的数字该bit为 0,一个为1  的情况。或者全1  的情况。 
            if(temp0!=0){//则说明有两个 出现一次的数字在bit=0的类,另一个在bit=1的类中,且值为temp1. ,否则什么也不做。
                *first=temp1;
                //找到一个数之后,,将此数放在数组最后一个位置,然后在数组的前n+1的元素中寻找。,然后调用在数组中有两个数字出现一次的函数即可解决问题。
                arr[len]=temp1; 
                findTwoNumInArrayOnlyOnce(arr,len+1,second,third); 
                return;//返回即可。 
            } 
        }
        else{//count1为偶数,即出现第二种情况:有两个出现一次的数字该bit为 1,一个为0  的情况。 或者全0 的情况。
            if(temp1!=0){//则说明有两个 出现一次的数字在bit=1的类,另一个在bit=0的位,且值为temp0. ,否则什么也不做。 
                *first=temp0;
                arr[len]=temp0; 
                findTwoNumInArrayOnlyOnce(arr,len+1,second,third);
                return ;//返回即可。 
            } 
        }
    }
}
int main(void){
    int n;
    while(scanf("%d",&n)!=EOF&n>0){
        int *arr=(int *)malloc((n+1)*sizeof(int));//多开辟一个空间,将找到的第一个数加入到最后一个位置,使得,这个数在数组中出现两次,进而方便寻找后面两个只出现一次的数字。 
        if(arr==NULL){
            exit(EXIT_FAILURE);
        }
        int val;
        for(int i=0;i<n;i++){
            scanf("%d",&val);
            arr[i]=val;
        }
        int first,second,third;
        findThreeNumInArrayOnlyOnce(arr,n,&first,&second,&third);
        printf("%d %d %d\n",first,second,third); 
    } 
    return 0;
}


来源:我是码农,转载请保留出处和链接!

本文链接:http://www.54manong.com/?id=1284

'); (window.slotbydup = window.slotbydup || []).push({ id: "u3646208", container: s }); })();
'); (window.slotbydup = window.slotbydup || []).push({ id: "u3646147", container: s }); })();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值