zoukankan      html  css  js  c++  java
  • 数组中只出现一次的数字

    一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

    数字都出现两次,则异或肯定为0。

    考虑下这个题目的简化版——数组中除一个数字只出现1次外,其它数字都成对出现,要求尽快找出这个数字。根据异或运算的特点,直接异或一次就可以找出这个数字。

    现在数组中有两个数字只出现1次,直接异或一次只能得到这两个数字的异或结果,但光从这个结果肯定无法得到这个两个数字。因此我们来分析下简化版中“异或”解法的关键点,这个关键点也相当明显——数组只能有一个数字出现1次

        设题目中这两个只出现1次的数字分别为A和B,如果能将A,B分开到二个数组中,那显然符合“异或”解法的关键点了。因此这个题目的关键点就是将A,B分开到二个数组中。由于A,B肯定是不相等的,因此在二进制上必定有一位是不同的。根据这一位是0还是1可以将A,B分开到A组和B组。而这个数组中其它数字要么就属于A组,要么就属于B组。再对A组和B组分别执行“异或”解法就可以得到A,B了。而要判断A,B在哪一位上不相同,只要根据A异或B的结果就可以知道了,这个结果在二进制上为1的位都说明A,B在这一位上是不相同的。

        比如int a[] = {1, 1, 3, 5, 2, 2}

        整个数组异或的结果为3^5即 0x0011 ^ 0x0101 = 0x0110

        对0x0110,第1位(由低向高,从0开始)就是1。因此整个数组根据第1位是0还是1分成两组。

        a[0] =1  0x0001  第一组

        a[1] =1  0x0001  第一组

        a[2] =3  0x0011  第二组

        a[3] =5  0x0101  第一组

        a[4] =2  0x0010  第二组

        a[5] =2  0x0010  第二组

        第一组有{1, 1, 5},第二组有{3, 2, 3},明显对这二组分别执行“异或”解法就可以得到5和3了。

     1 class Solution {
     2 public:
     3     void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
     4         int temp=0;
     5         int n=data.size();
     6         if(n<2) return;
     7         int i,j;
     8         for(i=0;i<n;i++)
     9             temp^=data[i];
    10         for(j=0;j<sizeof(int)*8;j++){
    11             if((temp>>j)&1==1)
    12                 break;
    13         }
    14         for(i=0;i<n;i++){
    15             if((data[i]>>j)&1==1){
    16                 *num1^=data[i];
    17             }
    18             else
    19                 *num2^=data[i];
    20         }
    21     }
    22 };

    扩展:数组A中,除了某一个数字x之外,其他数字都出现了三次,而x出现了一次。请给出最快的方法找到x。

    我们换一个角度来看,如果数组中没有x,那么数组中所有的数字都出现了3次,在二进制上,每位上1的个数肯定也能被3整除。如{1, 5, 1, 5, 1, 5}从二进制上看有:

    1:0001

    5:0101

    1:0001

    5:0101

    1:0001

    5:0101

    二进制第0位上有6个1,第2位上有3个1.第1位和第3位上都是0个1,每一位上的统计结果都可以被3整除。而再对该数组添加任何一个数,如果这个数在二进制的某位上为1都将导致该位上1的个数不能被3整除。因此通过统计二进制上每位1的个数就可以推断出x在该位置上是0还是1了,这样就能计算出x了。

    推广一下,所有其他数字出现N(N>=2)次,而一个数字出现1次都可以用这种解法来推导出这个出现1次的数字。

     1 // 【白话经典算法系列之十七】<span style="color:#000000;font: 14px/23px arial; text-transform: none; text-indent: 0px; letter-spacing: normal; word-spacing: 0px; float: none; display: inline !important; white-space: normal; font-size-adjust: none; font-stretch: normal; -webkit-text-stroke- 0px;">数组中只出现一次的数</span>
     2 //  by MoreWindows( http://blog.csdn.net/MoreWindows ) 
     3 //  欢迎关注http://weibo.com/morewindows
     4 #include <stdio.h>
     5 #include <string.h>
     6 int FindNumber(int a[], int n)
     7 {
     8   int bits[32];
     9   int i, j;
    10   // 累加数组中所有数字的二进制位
    11   memset(bits, 0, 32 * sizeof(int));
    12   for (i = 0; i < n; i++)
    13     for (j = 0; j < 32; j++)
    14       bits[j] += ((a[i] >> j) & 1);
    15   // 如果某位上的结果不能被整除,则肯定目标数字在这一位上为
    16   int result = 0;
    17   for (j = 0; j < 32; j++)
    18     if (bits[j] % 3 != 0)
    19       result += (1 << j);
    20   return result;
    21 }
    22 int main()
    23 {
    24   printf("    【白话经典算法系列之十七】数组中只出现一次的数
    ");
    25   printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --
    ");
    26   printf(" -- http://blog.csdn.net/morewindows/article/details/12684497 -- 
    
    ");
    27 
    28   const int MAXN = 10;
    29   int a[MAXN] = {2, 3, 1, 2, 3, 4, 1, 2, 3, 1};
    30   printf("%d
    ", FindNumber(a, MAXN));
    31   return 0;
    32 }

    运行结果如下图所示:

    本文地址:http://blog.csdn.net/morewindows/article/details/12684497

  • 相关阅读:
    VS 2010下一次性配置opencv(32位和64位相同)
    模拟鼠标事件
    Main函数参数argc,argv说明
    Visual Studio 2010 LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏解决方案
    常量指针和指针常量
    strlen函数实现的几种方法
    杀死指定的进程名
    typedef和typename关键字
    如何理解dart的mixin
    c# 通过dllimport 调用c 动态链接库
  • 原文地址:https://www.cnblogs.com/zl1991/p/4777374.html
Copyright © 2011-2022 走看看