性质:
- a^b=b^a
- a^b^c=a^(b^c)
- a^0=a
- a^a=0;
在刷题上主要应用的是【异或】的自反省,即A^B^B=A,依此性质出现的题目层>出不穷
应用1:交换a与b
t=a^b;
a=a^t;
b=b^t;
应用2:在一列数组中,其中数字均成双出现,仅有一个数字仅出现一次,求该【单身狗】。
直接将整个数组异或求解,解便是该 【单身狗】
☆☆剑指 Offer 56 - I. 数组中数字出现的次数
一个整型数组
nums
里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
异或及按位分组——分治
思路
显然,对于nums
中仅有1个孤独数字,可直接通过全局异或得到该值。但如何求解2个不同孤独数字呢?应用上述思想,将数组按如下条件分组,便可求得:
- 相同数字分为同一组
- 孤独数字分为不同组
故该问题转化为如上两个子问题。
设对nums
异或所得为x
,两个【孤独数字】分别为a,b;将x按位展开
x
1
x
2
.
.
.
x
k
x_1x_2...x_k
x1x2...xk,显然有
a
i
a_i
ai^
b
i
=
x
i
b_i=x_i
bi=xi;取任意
x
i
x_i
xi==1,
a
i
与
b
i
a_i与b_i
ai与bi不同。故依据该位进行分组,该位为0的为一组,该位为1的一组,必然满足如上两个条件:
- 相同数字该位必然相同,故必然为同一组,成立
- 由于 x i x_i xi==1,表示在该位a,b并不相同,所以必然不为同一组,成立
算法
- 求x
- 找到x从左到右第一个为1的位
- 按位分组,并异或求值
实现
vector<int> singleNumbers(vector<int>& nums) {
int x = 0;
for (int v : nums)
x ^= v;
int div = 1;
while ((div & ret) == 0)
div <<= 1;
int a = 0, b = 0;
for (int n : nums)
if (div & n)
a ^= n;
else
b ^= n;
return {a, b};
}
复杂度分析
- 时间复杂度: O ( N ) O(N) O(N)
- 空间复杂度: O ( 1 ) O(1) O(1)
☆☆☆137. 只出现一次的数字 II
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
思路
XOR
该运算符用于检测出现奇数次的位:1、3、5 等。
以此类推,只有某个位置的数字出现奇数次时,该位的掩码才不为 0。
因此,可以检测出出现一次的位和出现三次的位,但是要注意区分这两种情况。
算法
AND 和 NOT
为了区分出现一次的数字和出现三次的数字,使用两个位掩码:seen_once
和 seen_twice
。
思路是:
- 仅当
seen_twice
未变时,改变seen_once
。 - 仅当
seen_once
未变时,改变seen_twice
。
位掩码 seen_once
仅保留出现一次的数字,不保留出现三次的数字。
实现
seen_once=seen_twice=0;
while(){
seen_once=~seen_twice & (seen_once ^ x); /* seen_twice未变时,改变seen_once */
seen_twice=~seen_once & (seen_twice ^ x);
}
复杂度分析
- 时间复杂度: O ( N ) O(N) O(N)
- 空间复杂度: O ( N ) O(N) O(N)
题目标题难度划分
星级 | 题目难度 |
---|---|
☆ | 简单 |
☆☆ | 中等 |
☆☆☆ | 困难 |
算法掌握难度程度划分
星级 | 掌握难度 |
---|---|
无 | 普通 |
❤ | 经典,易掌握 |
❤❤ | 经典,略难掌握 |
❤❤❤ | 困难 |