1. 问题背景与核心挑战
在算法面试和实际编程中,"找出数组中只出现一次的数字"是一类经典问题。当问题升级到"有两个数字各出现一次,其余数字均出现两次"时,常规的哈希表统计法就显得效率不足。这正是LeetCode第260题"只出现一次的数字 III"的核心难点。
我最初遇到这个问题时,第一反应是用unordered_map记录频率,但很快意识到这需要O(n)空间复杂度。直到深入研究位运算特性后,才发现原来可以用O(1)额外空间解决。这个解法巧妙利用了异或运算的四个关键特性:
- 任何数与0异或结果不变(a ^ 0 = a)
- 任何数与自己异或结果为0(a ^ a = 0)
- 异或满足交换律和结合律
- 异或结果的二进制表示中,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的所有差异位信息。我们需要:
- 找到a^b中任意一个为1的位(说明a和b在此位不同)
- 根据该位将数组分成两组
- 分别在两组中使用简单异或法
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) {
