位运算技巧:高效解决数组中只出现一次的数字问题

1. 问题背景与核心挑战

在算法面试和实际编程中,"找出数组中只出现一次的数字"是一类经典问题。当问题升级到"有两个数字各出现一次,其余数字均出现两次"时,常规的哈希表统计法就显得效率不足。这正是LeetCode第260题"只出现一次的数字 III"的核心难点。

我最初遇到这个问题时,第一反应是用unordered_map记录频率,但很快意识到这需要O(n)空间复杂度。直到深入研究位运算特性后,才发现原来可以用O(1)额外空间解决。这个解法巧妙利用了异或运算的四个关键特性:

  1. 任何数与0异或结果不变(a ^ 0 = a)
  2. 任何数与自己异或结果为0(a ^ a = 0)
  3. 异或满足交换律和结合律
  4. 异或结果的二进制表示中,1的位置代表两个操作数在该位不同

需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。

2. 位运算解法深度解析

2.1 基础异或解法铺垫

先看简单版问题:数组中只有一个数字出现一次,其他都出现两次。这时只需将所有数字异或,成对出现的数字会相互抵消,最终结果就是唯一的单次数字:

cpp复制int singleNumber(vector<int>& nums) {
    int res = 0;
    for(int num : nums) res ^= num;
    return res;
}

2.2 升级问题的关键突破

当有两个单次数字(假设为a和b)时,直接异或全体得到的是a^b。这个结果包含了a和b的所有差异位信息。我们需要:

  1. 找到a^b中任意一个为1的位(说明a和b在此位不同)
  2. 根据该位将数组分成两组
  3. 分别在两组中使用简单异或法
cpp复制vector<int> singleNumberIII(vector<int>& nums) {
    long diff = 0;
    for(int num : nums) diff ^= num;
    
    // 获取最右侧的1(两种方法)
    long mask = diff & -diff;  // 方法1:利用补码特性
    // mask = (diff ^ (diff - 1)) & diff; // 方法2
    
    int a = 0, b = 0;
    for(int num : nums) {

内容推荐

已经到底了哦
已经到底了哦