Leetcode-138: Single Number II (位运算好题)

Leetcode-137: Single Number I 是一个数出现一次,其他数出现两次。直接用异或就可。

int singleNumber(vector<int>& nums) {
   int result=0;
   for (size_t i=0; i<nums.size(); ++i) {
       result^=nums[i];
   }
   return result;
}

Leetcode-138: Single Number II

Given an integer array nums where every element appears three times except for one, which appears exactly once. Find the single element and return it.

Example 1:

Input: nums = [2,2,3,2]
Output: 3
Example 2:

Input: nums = [0,1,0,1,0,1,99]
Output: 99

Constraints:

1 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
Each element in nums appears exactly three times except for one element which appears once.

Follow up: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Single Number II 是一个数出现一次,其他数出现三次。有3种方案:
解法1:用multiset或multimap

int singleNumber(vector<int>& nums) {

    multiset<int> ms;
    for (size_t i=0; i<nums.size(); ++i)
            ms.insert(nums[i]);

    for (auto i:ms)
        if (ms.count(i)==1) return i;

     return -1;
}

解法2: 用位运算。因为int为32位,遍历32位,并记录所有数组元素在该位为1的有多少个。如果累计起来不是3的倍数,则要找的数在该位为1。

#include <iostream>
#include <set>
#include <vector>
using namespace std;

int singleNumber(vector<int>& nums) {
    int result=0;
    int len=nums.size();
    for (int i=0; i<32; ++i) {
        int count=0;
        int bit=0x1<<i;
        for (int j=0; j<len; ++j) {
            if (nums[j] & bit)
                count++;
        }
        if (count%3!=0) result|=bit;
    }

    return result;
}

int main()
{
    vector<int> a={2,2,3,2};
    cout<<singleNumber(a)<<endl;
    return 0;
}

解法3:用三进制法。
下面这个链接讲的好。

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

或者

int singleNumber(vector<int>& nums) {
    int len=nums.size();
    int one=0, two=0, three=0;
    for (int i=0; i<len; ++i) {
      two = two | (one & nums[i]);
      one = one ^ nums[i];
      three = one & two;
      one = one & ~three;
      two = two & ~three;
    }
    return one;
}

解法4:
考虑解法2, integer 每个bit位上累加的和只有3种可能,00,01,10。我们把高位设为a, 低位设为b,新来的数nums[i]对应位为c (c=0 or 1), 转后后的a为a1, 转换后的b为b1,那么对应该位的数字逻辑有:

a   b   c            a1    b1
0   0   0     =>   0     0
0   1   0    =>    0     1
1   0   0    =>    1     0         <=   a
0   0   1    =>    0     1                       <= b
0   1   1    =>    1     0         <=   a
1   0   1    =>    1     1                       <= b

那么a是1的位对应1 0 0 和 0 1 1 这2组。所以, $a = (a & ~b & ~c) | (~a & b & c) $。

同样,b是1的位对应0 0 1和1 0 1这2组。所以,$b = (~a & ~b & c) | (a & ~b & c) $。
最后,我们只需要看bit位上累加数字是1的位即可,所以返回b即可。

代码如下:可能还可以用卡诺图简化。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int a = 0, b = 0, a1, b1;
        for (int i = 0; i < nums.size(); i++) {
            c = nums[i];
            a1 = (a & ~b & ~c) | (~a & b & c);
            b1 = (~a & b & ~c) | (~a & ~b & c);
            a = a1;
            b = b1;
        }
        return b;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值