LeetCode-137 只出现一次的数字 II

转自网友题解

题目

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,3,2]
输出: 3

示例 2:

输入: [0,1,0,1,0,1,99]
输出: 99

解题思路

  • 二进制下不考虑进位的加法:本题为 136. 只出现一次的数字
    的拓展,136 题中我们用到了异或运算。实际上,异或运算的含义是二进制下不考虑进位的加法,即:0xor0 = 0 + 0 = 0, 0xor1 = 0 + 1 = 1, 1xor0 = 1 + 0 = 1, 1xor1 = 1 + 1 = 0(不进位)
  • 三进制下不考虑进位的加法:通过定义某种运算 #,使得 0 # 1 = 1,1 # 1 = 2,2 # 1 = 0。在此运算规则下,出现了 3 次的数字的二进制所有位全部抵消为 0,而留下只出现 1 次的数字二进制对应位为 1。因此,在此运算规则下将整个 arr 中数字遍历加和,留下来的结果则为只出现 1 次的数字。
  • 代码分析: 请结合代码注释和图表理解。
    • ones ^= num:记录至目前元素num,二进制某位出现1次(当某位出现3次时有ones=1 ,与twos=1共同表示「出现3次」);
    • twos |= ones & num:记录至目前元素num,二进制某位出现2次 (当某位出现2次时,twos=1ones=0);
    • threes = ones & twos:记录至目前元素num,二进制某位出现3次(即当onestwos对应位同时为1three=1)。
    • one &= ~threes, two &= ~threes:将ones,twos中出现了3次的对应位清零,实现「不考虑进位的三进制加法」。
  • 复杂度分析:
    • 时间复杂度O(N):遍历一遍 nums 需要线性时间复杂度;
    • 空间复杂度O(1):使用常数额外空间。

代码:
python

class Solution:
    def singleNumber(self, nums: [int]) -> int:
        ones, twos, threes = 0, 0, 0
        for num in nums:
            twos |= ones & num # 二进制某位出现1次时twos = 0,出现2, 3次时twos = 1;
            ones ^= num  # 二进制某位出现2次时ones = 0,出现1, 3次时ones = 1;
            threes = ones & twos # 二进制某位出现3次时(即twos = ones = 1时)three = 1,其余即出现1, 2次时three = 0;
            ones &= ~threes # 将二进制下出现3次的位置零,实现`三进制下不考虑进位的加法`;
            twos &= ~threes
        return ones

java

class Solution {
    public int singleNumber(int[] nums) {
        int ones = 0, twos = 0, threes = 0;
        for(int num : nums){
            twos |= ones & num;
            ones ^= num;
            threes = ones & twos;
            ones &= ~threes;
            twos &= ~threes;
        }
        return ones;
    }
}

进一步简化

  • 以上过程本质上是通过构建3个变量的状态转换表来表示对应位的出现次数:使所有数字“相加”后出现3N+1次的位ones=1,出现3N3N+2次的位为ones=0。由于three其实是ones & twos的结果,因此我们可以舍弃threes,仅使用onestwos来记录出现次数。
某位出现 1次 2次 3次 4次 5次 6次 ...
ones 1 0 0 1 0 0 ...
twos 0 1 0 0 1 0 ...
threes 0 0 1 0 0 1 ...
  • 代码分析:
    • ones = ones ^ num & ~twos:
      • num=1时,只当ones=twos=0时将ones1,代表出现3N+1次;其余置0,根据twos值分别代表出现3N次和3N+2次;
      • num=0时,ones不变;
    • twos = twos ^ num & ~ones
      • num=1时,只当ones=twos=0时将twos1,代表出现3N+2次;其余置0,根据ones值分别代表出现3N次和3N+1次;
      • num=0时,twos不变;

代码:
python

class Solution:
    def singleNumber(self, nums: [int]) -> int:
        ones, twos = 0, 0
        for num in nums:
            ones = ones ^ num & ~twos
            twos = twos ^ num & ~ones
        return ones

java

class Solution {
    public int singleNumber(int[] nums) {
        int ones = 0, twos = 0;
        for(int num : nums){
            ones = ones ^ num & ~twos;
            twos = twos ^ num & ~ones;
        }
        return ones;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容