• 【剑指Offer】40、数组中只出现一次的数字


    题目描述:

      一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度为O(n),空间复杂度为O(1)。

      解题思路:

      这道题目相对比较难,一般情况下,我们首先可以想到的是顺序扫描数组,但其时间复杂度为O(n^2)。进一步也可以想到用哈希表保存每一个数次出现的次数,但是这使用了辅助空间,空间复杂度为O(n)。显然均不满足题目要求。

      我们先来看一个比较简单的情况,如果数组中只有一个数字出现一次,其他都出现两次。那么我们应该可以想到异或运算。异或运算有一个比较好的性质是:相同为0,相异为1。也就是说,任何一个数字异或它自己都等于0,而0异或任何数都等于那个数。因此,我们从头到尾依次异或数组中的每个数字,那么最终结果刚好是那个只出现一次的数字,重复的数字在异或过程中被抵消了。

      这是一种比较巧妙的思路,然而,本题只出现一次的数字有两个,简单的异或无法解决。但是,借助这种思路,我们可以进一步分析,如果我们能把数组分成两个子数组,使每个子数组包含一个只出现一次的数字,而其他数字成对出现,那么我们通过上述解法就可以找到两个元素。

      具体思路是:我们首先仍然从前向后依次异或数组中的数字,那么得到的结果是两个只出现一次的数字的异或结果,其他成对出现的数字被抵消了。由于这两个数字不同,所以异或结果肯定不为0,也就是这个异或结果一定至少有一位是1,我们在结果中找到第一个为1的位的位置,记为第n位。接下来,以第n位是不是1为标准,将数组分为两个子数组,第一个数组中第n位都是1,第二个数组中第n位都是0。这样,便实现了我们的目标。最后,两个子数组分别异或则可以找到只出现一次的数字。

      举例:

      以{2,4,3,6,3,2,5,5}为例:

      我们依次对数组中的每个数字做异或运行之后,得到的结果用二进制表示是0010。异或得到结果中的倒数第二位是1,于是我们根据数字的倒数第二位是不是1分为两个子数组。第一个子数组{2,3,6,3,2}中所有数字的倒数第二位都是1,而第二个子数组{4,5,5}中所有数字的倒数第二位都是0。接下来只要分别两个子数组求异或,就能找到第一个子数组中只出现一次的数字是6,而第二个子数组中只出现一次的数字是4。

    //num1,num2分别为长度为1的数组。传出参数
    //将num1[0],num2[0]设置为返回结果

    public
    void FindNumsAppearOnce(int[] nums, int num1[], int num2[]) {
    /* 思路:数组中的元素先依次异或,相同为0,则得到的是两个只出现一次的数的异或结果 对于得到的异或结果,找到其第一个为1的位 该位为1,
    说明两个只出现一次的数该位不同,所以按照该位是0还是1将数组分成两部分 这样,出现两次的数字都会分到同一个部分,
    而两个只出现一次的数正好被分开,再各自异或可得结果 */
    int diff = 0; for (int num : nums) diff ^= num; diff &= -diff; for (int num : nums) { if ((num & diff) == 0) num1[0] ^= num; else num2[0] ^= num; } }

    a&(-a) 什么意思???

    lowbit
    就是二进制数低位连续的0加上一个1组成的数
    也可以理zd解成一个数由2构成的最大约数
    比如 20
    转换成二专进制就是10100(2) 最低位有2个0 那20&(-20)就是100(2),就是4
    原理是 c里面一个数的相反数补码表示属 自己查吧 ..

    按位知与
    因为数字在电脑中是以补码的形式存在的
    比如
    a = 10
    仅以8位举例
    正数
    a 原码 0000 1010 补码专与原码相等 0000 1010

    比如
    a = 10
    仅以8位举例
    正数
    a 原码 0000 1010 补码专与原码相等 0000 1010
    负数 补码为 它绝对值的反码最后+1
    -a 补码 a的反码 11110101 最后+1 1111 0110
    再进行按位与操属作 结果为 0000 0010 转换成整型 就是 2

  • 相关阅读:
    Kafka项目实践
    页级别的恢复
    Linux查看网卡流量(转)
    Linux Top 命令解析 比较详细(转)
    《神秘的程序员们》漫画26~28:《万年坑系列》 I、II、III(转)
    Linux概念架构的理解(转)
    Building Redis for use on Cygwin(转)
    2014值得期待的Erlang两本新书
    jps命令使用
    编译原理学习导论
  • 原文地址:https://www.cnblogs.com/ziytong/p/12828795.html
Copyright © 2020-2023  润新知