[LeetCode] 137. Single Number II (位操作)

传送门

Description

Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

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

思路

题意:给定一个数组,其中有一个数出现一次,其他数出现三次,要求在时间复杂度为O(n)空间复杂度为O(1)的条件下,找出这个出现一次的数。

题解:

方法一:因为除了一个数外,其他每个数都出现三次,因此枚举每一位二进制位,统计每一位上这些数为1的数总共有多少个,然后与3相模,如果余1,证明这个出现一次的数的二进制在这位上为1。(此法是通用方法,适用于除一个数外,其他数出现k次,那么只需模k即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
    //12ms
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for (int i = 0;i < 32;i++){
            int cnt = 0;
            int mask = 1 << i;
            for (int j = 0;j < nums.size();j++){
                if (nums[j] & mask){
                    cnt++;
                }
            }
            if (cnt % 3){
                res |= mask;
            }
        }
        return res;
    }
};

  

方法二:用两个变量记录所有数二进制位中哪些位为1出现一次,哪些二进制位为1出现两次,之所以只需两个,是因为同一个数最多只出现三次,因此我们可以选定状态 00 -> 01 -> 10来记录,那么我们用ones表示哪些位为1出现一次(模3后出现一次),用twos表示哪些位为1出现两次(模3后出现一次),当ones和twos某一二进制位上同时为1说明这位为1出现了三次,那么我们此时将ones和twos的这位二进制位清0,最后ones就是答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
    //9ms
    int singleNumber(vector<int>& nums) {
        int ones = 0,twos = 0,threes = 0;
        for (unsigned int i = 0;i < nums.size();i++){
            //以下两句代码不能颠倒次序,如若颠倒,则一个数先记录于ones,
            //然后twos的值依赖于ones及这个数,那么这个数就被统计了两次
            twos |= (nums[i] & ones);  //记录有哪些二进制位为1且出现两次存于twos
            ones ^= nums[i];           //记录有哪些二进制位为1且出现一次存于ones
 
            //以下三句是清零操作
            threes = ones & twos;
            ones &= ~threes;
            twos &= ~threes;
        }
        return ones;
    }
};

 

另外,方法二代码可精简如下:ones与twos的含义与上述相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
    //9ms
    int singleNumber(vector<int>& nums) {
        int ones = 0,twos = 0;
        for (unsigned int i = 0;i < nums.size();i++){
            //ones&~twos以及twos&~ones都是为了清零操作,两者二进制位都为1时清零
            ones = (ones ^ nums[i]) & (~twos);
            twos = (twos ^ nums[i]) & (~ones);
        }
        return ones;
    }
};

  

posted @   zxzhang  阅读(287)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
历史上的今天:
2016-08-19 树状数组求第k小的元素
点击右上角即可分享
微信分享提示

目录导航