• leetcode-137-Single Number II-第二种解法


    题目描述:

    详细的题目描述见上一篇博客《leetcode-137-Single Number II-第一种解法》,这里简单说一下。

    有一个数组,所有元素都出现了三次,除了一个元素只出现了一次。输出这个只出现一次的元素。

    要求时间复杂度O(n),空间复杂度O(1)。

     

    要完成的函数:

    int singleNumber(vector<int>& s) 

     

    说明:

    上一篇博客中提出的方法很容易理解,但是不是O(n)的时间复杂度,而是O(n^2),这点应该很多朋友都能看出来。

    今天给大家分享一个O(n)的方法,先贴出简洁的代码给大家欣赏一下。这个方法同样参考于discuss区。

     

    代码:

    int singleNumber(vector<int>& nums) 
    {
        int a = 0, b = 0;
        for (int i = 0; i < nums.size(); ++i) 
        {
            b = (b ^ nums[i]) & ~a;
            a = (a ^ nums[i]) & ~b;
        } 
        return b;
    }

    短短几行代码,简洁扼要地完成了任务。以下举例详细说明为什么能这样子做,以及推测要如何产生这样子的想法。

     

    举例说明:

    数组为[2,2,2,3],一共有四个元素,进行四次循环。

    第一次循环,b=(0000^0010)&1111=0010=2,a=(0000^0010)&1101=0000=0

    第二次循环,b=(0010^0010)&1111=0000=0,a=(0000^0010)&1111=0010=2

    第三次循环,b=(0000^0010)&1101=0000=0,a=(0010^0010)&1111=0000=0

    第四次循环,b=(0000^0011)&1111=0011=3,a=(0000^0011)&1100=0000=0

    不知道大家有没有发现,某个值nums[i]第一次出现的时候,b把它记录了下来,这时候a=0;接着第二次出现的时候,b被清空了,记录到了a里面;接着第三次出现的时候,b和a都被清空了。

    如果一个数组中,所有的元素除了一个特殊的只出现一次,其他都出现了三次,那么根据我们刚刚观察到的结论,最后这个特殊元素必定会被记录在b中。

    有些朋友会说,但是不一定数组都是刚好3个2都在一起,3个4都在一起,都能够满足刚刚这样子的做法。

    上上篇博客136题中,笔者本人提出了异或其实是满足交换律和结合律的,而且&这个操作也是满足交换律和结合律的,所以无论3个2会不会一起出现,结果都是会刚好抵消的。

    所以上述的方法可以解决这个问题。

     

    怎么想出这种方法的:

    其实discuss区的大神是设计了一种方法,借由这种方法推出了a和b的变换方式…

    我们想要达到的效果其实是——

                a  b

    初始状态      :   0   0

    第一次碰见某个数x:   0   x(把x记录在b中)

    第二次碰见某个数x:   x   0(把x记录在a中)

    第三次碰见某个数x:   0   0(把a和b都清空,可以处理其他数)

    还记得我们之前处理“所有元素都出现两次,只有一个特殊元素出现一次”的问题吗?其实我们那会想要达到的状态也是——

                a  

    初始状态      :   0

    第一次碰见某个数x:   x(把x记录在a中)

    第二次碰见某个数x:   0(把a清空)

    然后我们刚好就找到了异或运算可以处理这个问题。

    那么这次我们同样利用异或运算,看能不能设计出一种变换的方法让a和b按照上述变换规则,进行转换。

    b=0时碰到x,就变成x;b=x时再碰到x,就变成0,这个不就是异或吗?所以我们也许可以设计b=b xor x。

    但是当b=0时再再碰到x,这时候b还是要为0,但这时候不同的是a=x,而前两种情况都是a=0。所以我们可以设计成:b=(b xor x)&~a

    同样道理,我们可以设计出:a=(a xor x)&~b

    在这种变换规则下,a和b都能按照我们设定的状态来发生转化。最后那个只出现一次的元素必定存储在b中。

     

    感想:

    异或真的是个神奇的东西,它其实已经内含了“判断”的过程。想一下我们“所有元素都出现两次,只有一个特殊元素出现一次”的问题,我们如果设计一个int型整数result用来记录,假定数组为[2,3,4,2,3],我们当然可以不断地result+2+3+4,但是到了第二次出现2的时候,要怎么判断这个2已经出现过了,这时候要result-2呢?但是异或就可以,只要你出现过一次,它就会永久记得你。

    话说异或是怎么实现的?我记得好像跟二进制加法有关?

  • 相关阅读:
    Spring Boot配置过滤器的两种方式
    Redis工具类封装RedisUtils
    Android_开机动画
    Android_OTA升级
    QT_学习笔记
    松翰单片机_SN8F570310——ADC
    全志_基于dts设备树驱动开发
    松翰单片机_SN8F570310
    松翰单片机_SN8F570310——GPIO
    松翰单片机_SN8F570310——COM & OPA
  • 原文地址:https://www.cnblogs.com/chenjx85/p/8763000.html
Copyright © 2020-2023  润新知