zoukankan      html  css  js  c++  java
  • 找出唯一出现一次的数

    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
    ");
        else
            printf("only num is : %d
    ", 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
    ", len);
        for (i = 1; i < sizeof(array) / sizeof(int); i++)
        {
            printf("ret is %d
    ", ret);
            ret = ret ^ array[i];
        }
    
        return ret;
    }
    
    int 
    main(void)
    {
        int ret = get_only2();
        printf("only number is %d
    ", 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
    ", a, b);
    
        swap(a, b);
    
        printf("after swap:  a %d, b %d
    ", 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
    ", A, B);
        else
            printf("wrong args
    ");
    
        return 0;
    }
    View Code

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

      思路1):同样如果按照上边思路来,假设三个不同的值是A, B, C。首先得到 rst = A^B^C

      找到rst的十六进制从右到左第一个1。如果A,B,C在这个位上的值为分别001或010或100。则这种情况下仍可以求出一个mask,

      进而可以将数列分成两组,其中一个必定包括在该位为1的数,假设为A,然后遍历数组,将 arr[idx] & mask != 0的所有值异或,这样便得到了A,然后就简化成了第二个问题。

      但是rst的十六进制从右到左第一个1,还可能是111这个情况,这样一来便无法用思路1来解,因为我们不能知道A,B,C的第x位是否是111,还是001或010或100,只有是后者才能用常规思路。

      到这里得到结论 思路1是行不通的。那么看思路2.

      

      思路2):

      (1)另x = (A ^ B), y = (B ^ C), z = (C ^ A)

      (2)我们有 (A^B)^(B^C)^(C^A) = x ^ y ^ z = (rst ^ C) ^(rst ^ A) ^(rst ^ B) = 0

      (3)x != 0, y != 0, z != 0 , 且 x != y != z, 因为两个相等的数 异或 才等于零,而A, B, C是互不相等的。

      (4)x ^ y ^ z = 0 且 x != y != z != 0, 那么x, y, z每位上的组合只能是000或110,这样才能保证x ^ y ^ z = 0,

         同时又不可能所有位组合全为 000,那样 x = y = z = 0与已知矛盾.由此得  x, y, z中十六进制从右向左数第一次肯定会出现110。

        假设首次出现110组合是从右向左数第m位,且两个1来自 x和y,则可以得到z的m位是0,并且,z第一次出现1的位置在m的左边,即可能是

        从右向左数(m + k = n, k > 0)的位置。举例 若x = 0x1001, y = 0x0001, 则一定有z = 0x1000。

        好了,在继续解释之前,我们要知道 rst ^ arr[i] 的结果集中,肯定包含x, y, z.因为x = rst ^ C, y = rst ^ A, z = rst ^ B,而A, B, C是arr中元素,

        同时,如果我们将 rst ^ arr[i]得到结果集中的每个新元素,按照其从右向左数第一次出现的1是否在第n位上进行分组,则可以得到两组数v1,v2,其中一组包含

        z,另外一组包含x,y。假设包含z的为v1, 包含x,y的为v2. 那么同时这样又将arr数组元素分为两组arr1, arr2,其中arr1中包含B, arr2中包含A,C.我们将 arr1中的所有

        元素异或操作,从而可以得到B。

      (5)找到一个了,另外两个就好找了。

    #include <stdio.h>
    
    struct diff_values
    {
        int first;
        int second;
        int third;
    };
    
    static int
    get_oxr_rst(int *arr, int len)
    {
        int rst = 0;
        int idx;
        
        for(idx = 0; idx < len; idx ++)
        {
            rst ^= arr[idx];
        } 
    
        return rst;
    }
    
    //a new method to get the mask without using a loop
    static int
    get_first_onebit_mask(int value)
    {
        return value & ~(value - 1);
    }
    
    static int
    get_mask(int *arr, int len, int oxr_rst)
    {
        int idx;
        int mask = 0;
    
        /*
             if arr[i] = arr[j] then 
                 oxr_rst ^ arr[i] = oxr_rst ^ arr[i] then
                 get_first_onebit_mask(oxr_rst ^ arr[i]) = get_first_onebit_mask(oxr_rst ^ arr[j]) then
                 this operation cross out all arr[i] = arr[j].
             then we get:
                 mask = 
                 get_first_onebit_mask(oxr_rst ^ (arr[i])  //(arr[i] = A)
                 ^ get_first_onebit_mask(oxr_rst ^ arr[j]) //(arr[j] = B)
                 ^ get_first_onebit_mask(oxr_rst ^ arr[k]) //(stt[k] = C)
            then mask is what we want whose first "1" is just on postion n
        */
        for (idx = 0; idx < len; idx++)
        {
            //mask ^= get_first_onebit_mask(oxr_rst ^ arr[idx]);
    
            int tmpmask = get_first_onebit_mask(oxr_rst ^ arr[idx]);
            mask ^= tmpmask;
        }
    
        return mask;
    }
    
    static int 
    get_rst2(int *arr, int len, struct diff_values &dv)
    {
        if (len < 2)
        {
            return 0;
        }
    
        int oxr_rst = get_oxr_rst(arr, len);
        int mask = get_first_onebit_mask(oxr_rst);
    
        int idx;
        int first = 0;
    
        for (idx = 0; idx < len; idx++)
        {
            if (mask & arr[idx])
            {
                first ^= arr[idx];
            }
        }
    
        dv.second = first;
        dv.third = first ^ oxr_rst;
    
        return 1;
    }
    
    int
    get_rst(int *arr, int len, struct diff_values &dv)
    {
        if (len < 3)
        {
            return 0;
        }
    
        int oxr_rst = get_oxr_rst(arr, len);
        int mask = get_mask(arr, len, oxr_rst);
    
        int idx;
        int first = 0;
        for (idx = 0; idx < len; idx++)
        {
            if (get_first_onebit_mask(oxr_rst ^ arr[idx]) == mask) //this operation equals to get v1.
            {
                first ^= arr[idx]; //this equals to the oxr operation in arr1 which contains B as the example
                                  // and the result is one of value of A, B , C.  
            }
        }
        
        //when we get there, it means we get one of the three unique numbers;
        //then we get the left two numbers;
    
        //exchange first with arr[len - 1], 
        for (idx = 0; idx < len; idx++)
        {
            if (first == arr[idx])
            {
                arr[idx] = arr[idx] ^ arr[len - 1];
                arr[len - 1] = arr[idx] ^ arr[len - 1];
                arr[idx] = arr[idx] ^ arr[len - 1];
                break;
            }
        }
    
        dv.first = first;
    
        //only pass len - 1 values into arr, so that we can delete value first
        get_rst2(arr, len - 1, dv);
    
        return 1;
    }    
    
    int
    main(int argc, char *argv[])
    {
        int arr[] = { 3, 3, -4,5, 5, 8, 9 };
        struct diff_values dv;
    
        int rst = get_rst(arr, sizeof(arr) / sizeof(int), dv);
    
        if (rst)
            printf("different number is A %d , B %d , C %d
    ", dv.first, dv.second, dv.third);
        else
            printf("wrong args
    ");
    
        return 0;
    }

     参考:

      http://www.cnblogs.com/luxiaoxun/archive/2012/09/08/2676610.html

      https://www.lijinma.com/blog/2014/05/29/amazing-xor/

      http://baike.baidu.com/link?url=SeBrbp2jNqe1N-hF_j4UWt953O7ra73Seo__jW2uEbXeRuo-wLTKkROZZAV_jCTLICKw7oSQQRQmZovRCaFYkcC3MIS2jSd7wR9kVVIbjKm

      

    以后碰到类似的题,继续补充,欢迎指正与指点。

      

      

      

       

     

      

  • 相关阅读:
    postgreSQL入门命令
    JDBC连接数据库
    nano编辑器使用教程
    Linux上vi(vim)编辑器使用教程
    【编程思想】【设计模式】【行为模式Behavioral】状态模式State
    【编程思想】【设计模式】【行为模式Behavioral】Specification
    【编程思想】【设计模式】【行为模式Behavioral】registry
    【编程思想】【设计模式】【行为模式Behavioral】Publish_Subscribe
    【编程思想】【设计模式】【行为模式Behavioral】观察者模式Observer
    【编程思想】【设计模式】【行为模式Behavioral】备忘录模式Memento
  • 原文地址:https://www.cnblogs.com/newbeeyu/p/5898258.html
Copyright © 2011-2022 走看看