找出唯一出现一次的数

Posted mr_yu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了找出唯一出现一次的数相关的知识,希望对你有一定的参考价值。

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;
    }
}

int
main(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>

void
swap(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>

int
get_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;
}

int
get_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;
}    

int
main(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

 

未完,待续。

  

  

  

   

 

  

 

以上是关于找出唯一出现一次的数的主要内容,如果未能解决你的问题,请参考以下文章

面试题---找出数组中两个只出现一次的数

算法编程找出仅仅出现一次的数-singleNumber

136 Single Number 数组中除一个数外其他数都出现两次,找出只出现一次的数

找出2*n+1个数中,只出现了一次的数,其他的数字都出现了两次

找出数组中只出现一次的数字

力扣第136题 只出现一次的数