[LeetCode每日一题] 137. Single Number II

186 阅读1分钟

解法一,哈希表。时间复杂度O(n)O(n),空间复杂度O(n)O(n)

var singleNumber = function(nums) {
    let num_cnt = new Map()
    for (let num of nums) {
        if (num_cnt.has(num)) num_cnt.set(num, num_cnt.get(num)+1)
        else num_cnt.set(num, 1)
    }
    for (let num of num_cnt.keys()) {
        if (num_cnt.get(num) === 1) return num
    }
};

解法二,位运算。将所有数看看作是32位的二进制数,对于每一位,计算该位上1有多少个,如果刚好是3的倍数个,说明要找的数该位上为0,否则为1.由于每个数视作32位,那么时间复杂度O(n)O(n),空间复杂度O(1)O(1)

var singleNumber = function(nums) {
    let res = 0
    for (let i = 0; i < 32; i++) {
        let cnt = 0
        for (let num of nums) cnt += num&(1<<i)?1:0
        res += (cnt%3)<<i
    }
    return res
};

解法三,基于有限状态自动机的位运算,参考了 只出现一次的数字 II(有限状态自动机 + 位运算,清晰图解).大致思路如下:对于每一位,一开始是0,每碰到一个1就++,当加到3的时候就归零。于是我们用两位two和one来表示,00->01->10->00.具体思路可以看原文章。这种方法难点在于如何通过真值表构建运算关系。

var singleNumber = function(nums) {
    let ones = 0, twos = 0
    for (const num of nums) {
        ones = ones^num&~twos
        twos = twos^num&~ones
    }
    return ones
};

再来看两道相似题。

第一题136. Single Number,比较简单。

var singleNumber = function(nums) {
    return nums.reduce((prev,next)=>prev^next, 0)
};

第二题260. Single Number III,这题我们先对所有数进行异或得到sum,sum=a^b,由于a!=b,那么sum肯定不为0,说明sum的二进制表示中至少有一位为1,即a和b在这一位上一个为0一个为1.我们根据这一位将所有数分成两拨,一拨是这一位上为0的数,另一拨是这一位上为1的数,a和b分别在这两拨中。现在每一拨都是上面一题,答案呼之欲出。

var singleNumber = function(nums) {
    let sum = nums.reduce((prev,next)=>prev^next, 0)
    for (let i = 0; i < 32; i++) {
        if (sum&(1<<i)) {
            let a = 0, b = 0
            for (let num of nums) {
                if (num&(1<<i)) a ^= num
                else b ^= num
            }
            return [a,b]
        }
    }
};