LeetCode 137. 只出现一次的数字 II —— 位运算解法

题目描述

给你一个整数数组 nums ,除某个元素仅出现一次外,其余每个元素都恰出现三次。请你找出并返回那个只出现一次的元素。

你必须设计并实现 线性时间复杂度 且使用 常数级空间 的算法。

示例 1:

复制代码
输入:nums = [2,2,3,2]
输出:3

示例 2:

复制代码
输入:nums = [0,1,0,1,0,1,99]
输出:99

提示:

  • 1 <= nums.length <= 3 * 10^4

  • -2^31 <= numsi <= 2^31 - 1

  • 除某个元素仅出现一次外,其余每个元素都恰出现三次


解题思路

这道题是 位运算经典题 ,可以用 按位统计 的方法解决。

方法 1:按位统计(简单易理解)

  1. 统计每一位 1 的个数

    • int 有 32 位

    • 建一个 bit[32] 数组,表示每一位上 1 出现的次数

  2. 对 3 取模

    • 出现 3 次的数字在每一位上的 1 的个数是 3 的倍数

    • 只出现一次的数字会留下余数 1

  3. 还原结果

    • 将每一位余数为 1 的位置拼接起来,即为只出现一次的数字

C 语言实现

复制代码
int singleNumber(int* nums, int numsSize) {
    int bit[32] = {0};
    
    // 统计每一位1的个数
    for (int i = 0; i < numsSize; i++) {
        for (int j = 0; j < 32; j++) {
            bit[j] += (nums[i] >> j) & 1;
        }
    }

    int result = 0;

    // 取模3并还原结果
    for (int j = 0; j < 32; j++) {
        if (bit[j] % 3) {
            result |= (1u << j);   // 注意用 unsigned 避免符号位报错
        }
    }

    return result;
}

⚠️ 注意:

  • 1 << 31 可能会出现 未定义行为,因为 1 是 signed int

  • 1u << j 可以安全操作符号位


方法 2:状态机位运算(更优雅)

这个方法用 两个变量 onestwos 表示每一位出现次数的状态:

  • ones:该位出现过 1 次

  • twos:该位出现过 2 次

  • 当某位出现 3 次时,自动归零

核心公式:

复制代码
ones = (ones ^ num) & ~twos;
twos = (twos ^ num) & ~ones;

最终 ones 就是只出现一次的数字。

代码示例:

复制代码
int singleNumber(int* nums, int numsSize) {
    int ones = 0, twos = 0;
    for (int i = 0; i < numsSize; i++) {
        ones = (ones ^ nums[i]) & ~twos;
        twos = (twos ^ nums[i]) & ~ones;
    }
    return ones;
}
  • 时间复杂度:O(n)

  • 空间复杂度:O(1)


总结

  • 本题核心是 位运算 + 计数取模

  • 方法 1 用 32 位数组,易理解,但空间稍大

  • 方法 2 用 状态机位运算,常数空间,面试更加加分

  • 注意 符号位左移 的写法,要用 unsigned int 避免运行错误


💡 拓展思路

  • 如果数字出现次数不是 3,而是其他 k,按位统计仍然适用

  • 方法 2 的状态机也可以扩展到 k 次出现的情况

相关推荐
SoftLipaRZC22 分钟前
单链表的应用:经典OJ题与通讯录项目实战
数据结构
SoftLipaRZC26 分钟前
单链表专题:从概念到实现
数据结构
折哥的程序人生 · 物流技术专研6 小时前
Java面试85题图解版 · 特别篇:2026后端高频面试题复盘(算法底层逻辑+高并发架构设计全解析,附Java实战代码)
java·网络·数据库·算法·面试
玖玥拾7 小时前
C/C++ 基础笔记(十四)多态与模板编程
c语言·c++·多态·模板
想吃火锅10058 小时前
【leetcode】14.最长公共前缀js
算法·leetcode·职场和发展
云絮.9 小时前
数据库操作
数据库·mysql·算法·oracle
小林ixn9 小时前
LeetCode 206. 反转链表(迭代 + 递归详解)
算法·leetcode·链表
凡人叶枫9 小时前
Effective C++ 条款17:以独立语句将 newed 对象置入智能指针
java·linux·开发语言·c++·算法
菜鸟‍11 小时前
LeetCode 1 27 和 704 || 两数之和 移除元素 二分查找
算法·leetcode·职场和发展
caimouse11 小时前
Reactos 第1章 概述
c语言·开发语言·架构