【leetcode】137.Single number II

原题链接:https://leetcode-cn.com/problems/single-number-ii/

①异或运算(XOR)

可以这么理解:xor是一个半加器,即做不进位的加法。

应用举例:交换a,b的值,一般会这么写:

int tmp=a;
a=b;
b=tmp;

而当初wzh告诉我还可以这么写:

a=a^b;
b=a^b;
a=a^b;

惊为天人了。

②回到这道题。最不济感觉应该也可以用用空间换时间的方式,即打一个int数组,开到数据范围那么大,然后统计出现次数就行了。但是数据范围过大。由于之前对异或的了解,也知道要用异或来做,但是只会类似136题。

于是查找资料,发现了一堆“有限状态自动机”“哈希表”之类听不懂的名词。

首先弄懂的方法:

将数写成二进制的形式,然后位运算统计每一位出现的次数。

例如:对于数据10,10,6,10,二进制为1010,1010,0110,1010

我们开个数组统计各位出现1的次数。最高位到最低位出现1的次数分别为3,1,4,0

我们将它模3,得到0,1,1,0,就是我们要求的数了,原理非常简单。但是时间复杂度较高。

class Solution {
public:
    int singleNumber(vector<int>& nums) {int bittime[32]={0}; 
        for(int i=0;i<nums.size();i++){
            for(int j=0;j<32;j++){
                if(nums[i]&(1<<j))bittime[j]++;
            }
        }
        int res=0;
        for(int i=0;i<32;i++){
            res|=(bittime[i]%3)<<i;
        }
        return res;
    } 
};

③好的,现在来刚一下位运算:

再见,刚不出来,占坑,等我学了数电再来填坑。

时隔一年我回来填坑了,数电期末也是爆炸只有90.但是这不妨碍我学会了这一方法!现在我就来整理一下思路。

参考:https://www.cnblogs.com/bjwu/p/9323808.html 以及leetcode题解

首先,前面的分析也提到了二进制位统计1的方法,那么这题实际上是每一位上的1的个数模3,也就是说这题完全可以用数电里的mealy型状态机来做,这应该就是所谓FSM,有限状态机吧。

按照传统的解数电题的思路,我们列出状态转移图:

 由于使用了3种状态所以使用2个二进制位进行编码:

当前状态 输入 下一状态与输出
00 0 00/0
00 1 01/1
01 0 01/1
01 1 10/0
10 0 10/0
10 1 00/0
11 无关状态  
11 无关状态  

 

 

 

 

 

 

 

 

 

 

 

然后是卡诺图:

 

 于是,Q1n+1=X'Q1+XQ0  , Y=Q0n+1=X'Q0+Q1'Q0'X

快乐写代码:

class Solution {
public:
    int q1 = 0;
    int tmp = 0;
    int q0 = 0;
    int singleNumber(vector<int>& nums) {
        for(int i=0; i<nums.size(); i++){
            tmp = q1;
            q1 = (~nums[i]&tmp) | (nums[i]&q0);
            q0 = (~nums[i]&q0) | (~tmp&~q0&nums[i]);
        }
        return q0;
    }
};

顺利通过

但是这里用了一个额外变量tmp,而最优秀的答案是这样的:

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        seen_once = seen_twice = 0
        
        for num in nums:
            # first appearance: 
            # add num to seen_once 
            # don't add to seen_twice because of presence in seen_once
            
            # second appearance: 
            # remove num from seen_once 
            # add num to seen_twice
            
            # third appearance: 
            # don't add to seen_once because of presence in seen_twice
            # remove num from seen_twice
            seen_once = ~seen_twice & (seen_once ^ num)
            seen_twice = ~seen_once & (seen_twice ^ num)

        return seen_once

刚看到时我的心情和热门评论一样:

 

 仔细分析其实挺有道理的,但是我想不到这么高级的方法。

就这样吧。

 

 

 

 

posted @ 2020-05-08 18:20  buptzsc  阅读(262)  评论(0)    收藏  举报