当前位置: 首页 > news >正文

只出现一次的数字

1. 两个数字出现一次,其余数字出现两次。O(n)+O(1)。

找一个不同点,将所有数字分为两类。其中,两个只出现一次的数字各出现在一个集合当中。

如何找最后一个不同点?我们知道,如果两个数字不同的话,那么,在二进制表示下,他们一定有一个二进制位是不同的,其中一个为 0 0 0,一个为 1 1 1。通过对所有数字求异或和,就可以得到 a a a ^ b b b,然后枚举一下,任意找一个不同点即可!

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int xors = 0;  
        for(auto &x : nums) xors ^= x;
        int diff = 0; // 二进制表示中第一个不同点
        for(int i = 0; i < 32; i ++ ) {
            if(xors >> i & 1) {
                diff = i;
                break;
            }
        }
        int r1 = 0, r2 = 0;
        for(auto &x : nums) {
            if(x >> diff & 1)   r1 ^= x;
            else    r2 ^= x;
        }
        return {r1, r2};
    }
};

2. 一个数字出现了一次,其余都出现了三次

我们首先从暴力的角度出发,我们可以枚举每个 bit 位置 1 的数量,如果该 bit 位置 1 出现了 3 次,就消为 0,这可以通过对 3 取模实现。时间复杂度是 O ( 32 N ) O(32N) O(32N)

class Solution {
public:
    int trainingPlan(vector<int>& actions) {
        int res = 0;
        for(int i = 0; i < 32; i ++ ) {
            int cnt = 0;
            for(auto &x : actions)  cnt += x >> i & 1;
            cnt %= 3;
            res += cnt << i;
        }
        return res;
    }
};

那么,我们有没有办法不枚举每个 bit 位置,而是一次性直接对数字的所有 bit 位置进行位运算,将时间复杂度优化到 O ( N ) O(N) O(N) 呢?

试想一下,我们可以设计一种逻辑运算,当某个 bit 位置的 1 出现 3 次时,该逻辑运算的结果是 0,其他情况下为 1,这样我们对所有数字进行该逻辑运算,得到的结果就是那个只出现一次的数字。

在这里我们可以定义两个变量 onestwos,分别记录 bit 位 1 出现一次的位和 1 出现两次的位。注意在这里我们无需记录 1 出现了三次的 bit 位,因此在我们的逻辑运算中,出现了三次的 bit 位会计算为 0,也即出现 0 次。由于 0 比较特殊,这里我们无需显示额外记录。那么最后 ones 即为我们想要的答案。

例如,ones=10=1010B。其中,bit 位置为 1 和 4 的位为 1,表示这两个 bit 位 1 出现的次数为 1。

另外,bit 位置为 0 和 3 的位为 1,表示这两个 bit 为 1 出现的次数为 0,它包含在 ones 中隐式表示了。

为了得到这种逻辑运算,我们可以运用数字电路中的卡诺图,一种用于简化布尔表达式的方法,它可以帮助我们直观地找出最简逻辑表达式。

通过上面的分析,这里有三种状态:[0, 1, 2],分别表示 bit 位中 1 出现的次数为 0 次,1次,2次。我们用 ones 表示 1,twos 表示 2,0 隐式表示。

我们可以将 onestwos 分别视为两个二进制位的低位和高位,在接受一个新数字时,我们就可以得到 onestwos 的卡诺图。

# AB分别表示twos、ones
# AB为11的结果是无意义的,用x表示
# 10在接受输入1之后变成00,表示出现3次1的bit为又化为0了

# A 的卡诺图
AB\C |  0  |  1
=================
  00 |  0  |  0
  01 |  0  |  1        ====> 得到 A = BC + AC'
  11 |  x  |  x
  10 |  1  |  0

# B 的卡诺图
AB\C |  0  |  1
=================
  00 |  0  |  1
  01 |  1  |  0        ====> 得到 B = BC' + A'B'C
  11 |  x  |  x
  10 |  0  |  0
class Solution {
public:
    int trainingPlan(vector<int>& nums) {
        int A = 0, B = 0;
        for (auto &C : nums) {
            int preA = A;
            A = (B & C) | (A & ~C);
            B = (B & ~C) | (~preA & ~B & C);
        }
        return B;
    }
};

相关文章:

  • 为AI聊天工具添加一个知识系统 之150 设计重审 之15 完整方案及评估 之3
  • 【mybatis使用小知识合集持续更新】
  • 283.移动零解题记录
  • 深入解析 MyBatis-Plus 批量操作:原理、实现与性能优化
  • Matplotlib.day16
  • Nextjs15 - 什么是CSR、SSR、SSG和ISR
  • centos 7 搭建ftp 基于虚拟用户用shell脚本搭建
  • k8s存储介绍(六)StorangeClass
  • Redis :command not allowed when used memory
  • a, b = map(int, input().split()) 从用户输入中读取两个整数
  • 耘想Docker LinNAS,颠覆传统存储体验!
  • muduo库的思路梳理
  • 前端使用WPS WebOffice 做在线文档预览与编辑
  • Redux,React-redux。基础
  • 【脏读、不可重复读、幻读区别】
  • 云端陷阱:当免费午餐变成付费订阅,智能家居用户如何破局?
  • 【48】指针:函数的“数组入口”与“安全锁”——数组参数传递
  • 【Linux】嵌入式Web服务库:mongoose
  • pytorch与其他ai工具
  • 什么是异步编程,如何在 JavaScript 中实现?
  • 泽连斯基表示将在土耳其“等候”普京
  • 同济大学原常务副校长、著名隧道及地下工程专家李永盛逝世
  • 外交部发言人就印巴局势升级答记者问
  • 悬疑推理联合书单|虫神山事件
  • 美乌基金协议:美国搞了一套可在资源富集地区复刻的商业模式
  • 普雷沃斯特当选新一任天主教罗马教皇