首页 > 代码库 > 找出唯一出现一次的数

找出唯一出现一次的数

1.问题描述:找出数列中唯一一个出现一次的数,其余得数都出现两次。

分析:

  1)最笨的方法当然是穷举了:

#include <stdio.h>int array[] = { 1 , 3, 3, 1, 5, 5 , 6};int get_only(){    int *ptmp1, *ptmp2;    int *arrend = array + sizeof(array) / sizeof(int);    for(ptmp1 = array; ptmp1 != arrend; ptmp1 ++)    {        for (ptmp2 = array; ptmp2 != arrend; ptmp2++)        {            if (ptmp2 != ptmp1 && *ptmp2 == *ptmp1)            {                break;            }        }        if (ptmp2 == arrend)        {            return ptmp1 - array;        }    }     if (ptmp1 == arrend)    {        return -1;    }}intmain(int argc, char *argv[]){    int rst = get_only();    if (rst == -1)        printf("no such number\n");    else        printf("only num is : %d\n", array[rst]);    return 0;}

  2)异或操作 (^):

  性质: 0 ^ 0 = 0, 1 ^ 1 = 0, 0 ^ 1 = 1, 0 ^ A = A;

  总结起来就是按位异或 相同为0,不同为1。

  这样一来明显得出,相同的俩个数异或得0,不同的两个数异或一定不为0(多少另算),0和任和数异或都得其本身(0 ^ A = A)。

  由此得出另一种方法,将数组中的所有元素进行异或操作,则剩下的一个必定是唯一一个只出现一次的数:

#include <stdio.h>int array[] = { 1 , 3, 3, 1, 5, 5 , 6 };int get_only2(){    int ret = array[0];    int i;    printf("len is %d\n", len);    for (i = 1; i < sizeof(array) / sizeof(int); i++)    {        printf("ret is %d\n", ret);        ret = ret ^ array[i];    }    return ret;}int main(void){    int ret = get_only2();    printf("only number is %d\n", ret);    return 0;}

  btw:运用异或操作还可完成不借助其它参数,交换两个数的值: (用 g++编译,gcc编译不过)

#include <stdio.h>voidswap(int &a, int &b){    a = a ^ b;    b = a ^ b;    a = a ^ b;}int main(void){    int a = 1;    int b = 2;    printf("before swap: a %d, b %d\n", a, b);    swap(a, b);    printf("after swap:  a %d, b %d\n", a, b);    return 0;}

 

-----------------------------------------------------------------------分割线----------------------------------------------------------

2.问题描述:数列中,只有两个整数只出现一次,其余的出现两次,找出这两个整数.

  有了上边的铺垫,很容易想到用 异或 操作,通常回想到将所有数异或。

  但是发现,若这两个数分别为A, B,则异或后 只能消除其它存在两次的数,但是结果为A ^ B,仍然找不出A, B。

  此时我们希望A和B, 分别出现在两个数组中,并且这两个数组中的各自的其它成员,是两两相同的。然后这两个数组分别各自异或操作,则数组各自只剩下A和B,这样便找出来了。

  但是我们如何分出这样的两个数组呢?

  根据之前分析得出,A和B不相等则 A ^ B 的结果必然不为0,那么这个结果必定可以用16进制表示,并且至少某一位上为1.我们假设从右向左的第x位不为0,即为1.(0x001001...,1的位置为x)

  那么肯定A的第x位为1,B的为0;或者相反。同理,除A,B以外的这些数的第x位上或者为0,或者为1. 此时便可根据第x位为0或者为1,将整个数列分成两个数组且分别包含A和B,姑且叫arrA, arrB。

  这样一分,我们还发现,arrA中除A,以外是两两相同的,arrB除B以外也是两两相同的。最极端的情况是arrA只包含A或者arrB只包含B。但是这不重要,重要的是我们得到了两个数组分别包含A和B且其余两两相同。

  于是,我们分别将arrA中元素异或操作得到A,将arrB中元素异或操作得到B。

  步骤:(g++编译)

  1)先得到A ^ B (get_oxr_rst)

  2)找到第x位上的1,这里的技巧是得到一个mask,只处理这一位 (get_mask)。

  3)用mask & arr[idx], 找到这一位为1的所有元素,并异或操作。

  4)B = orx_rst ^ A.

技术分享
#include <stdio.h>intget_oxr_rst(int *arr, int len){    int rst = 0;    int idx;        for(idx = 0; idx < len; idx ++)    {        rst ^= arr[idx];    }     return rst;}int get_mask(int oxr_rst){    int mask = 1;    while((mask & oxr_rst) == 0)     {        mask <<= 1;    }     return mask;}intget_rst(int *arr, int len, int &A, int &B){    if (len < 2)    {        return 0;    }    int oxr_rst = get_oxr_rst(arr, len);    int mask = get_mask(oxr_rst);    int idx;    for (idx = 0; idx < len; idx++)    {        if (mask & arr[idx])        {            A ^= arr[idx];        }    }    B = A ^ oxr_rst;    return 1;}    intmain(int argc, char *argv[]){    int arr[] = { -4, 2, 2, 4 , 3, 5, 5, 3};    int A = 0, B = 0;    int rst = get_rst(arr, sizeof(arr) / sizeof(int), A, B);    if (rst)        printf("different number is A %d and B %d\n", A, B);    else        printf("wrong args\n");    return 0;}
View Code

 

未完,待续。

  

  

  

   

 

  

 

找出唯一出现一次的数