认识异或
- 异或即无进位加法
0101 1011 ^ 1010 0101 = 1111 1110
- 满足交换律和结合律(阿贝尔群):
a ^ b = b ^ a
,(a ^ b) ^ c = a ^ (b ^ c)
- 单位元
0
,a ^ 0 = a
;逆元a
,a ^ a = 0
- 根据 3. 可推广为:偶数个
a
异或则会0
;奇数个a
异或则还是为a
swap
交换俩数
vector<int> swapNumbers(vector<int>& a) {
a[0] ^= a[1]; // a[0] = a[0] ^ a[1];
a[1] ^= a[0]; // a[1] = a[1] ^ a[0] ^ a[1]
// 交换律:a[1] = a[1] ^ a[1] ^ a[0]
// 逆元: a[1] = 0 ^ a[0]
// 单位元:a[1] = a[0]
a[0] ^= a[1]; // a[0] = a[0] ^ a[1] ^ a[0]
// 交换律:a[0] = a[0] ^ a[0] ^ a[1]
// 逆元:a[0] = 0 ^ a[1]
// 单位元:a[0] = a[1]
// 所以最终结果就是 a[0] = a[1], a[1] = a[0]
return a;
}
只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
int singleNumber(vector<int>& nums) {
int res = 0;
for (auto x : nums) res ^= x;
return res;
}
使用交换律将相同的两个数凑在一起,相同数互为逆元,全部变 0
,0
又是单位元,所以就剩下只出现一次的数。
所以可推广为:
给定一个非空整数数组,除了某个元素只出现奇数次以外,其余每个元素均出现偶数次。找出那个只出现了奇数次的元素。
只出现一次的数字 III
给定一个整数数组
nums
,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
// leetcode 有毒,卡边界
vector<int> singleNumber(vector<int>& nums) {
long eor = 0;
for (auto x : nums) eor ^= x; // 最后结果为 eor = one ^ two
long rightOne = eor & (~eor + 1); // 取出最右边的1,这个 1 的位置
// 对应于两个数位置上一是不同
long one = 0;
for (auto x : nums) {
if ((x & rightOne) != 0) { // 将两个奇数次数分开
one ^= x; // 得出第一个奇数次数
}
}
long two = eor ^ one; // 得出第二个奇数次数
return {(int)one, (int)two};
}
a: 1100 0011
b: 1100 1100
a^b: 0000 0011
^ 这个位置上,a 和 b 不一样
然后将两个数分开,根据性质就可以获取结果
所以可推广为:
给定一个整数数组
nums
,其中恰好有两个元素只出现奇数次,其余所有元素均出现偶数次。 找出只出现奇数次的那两个元素。
错误的集合
集合 s 包含从 1 到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。
无中生有
拿到一道新的问题,先在大脑中回想以前有没有做过的类似题目
看问题描述,返回答案是两个数:{重复的数,没有的数}
找规律:一个数字重复,那就是出现两次。没有出现的数,那就是出现零次
共同特点,两个数都是出现偶数次。如果我们找到这两个数,然后在数组中遍历一次就能分辨这两个数
所以,我们现在可以把问题转化为,在 数组中寻找两个出现偶次数的数
问题转化
在大脑中快速回想!有没有做过 寻找数组中只有两个出现偶次数的数 类似的题
果不其然!做过!寻找数组中只有两个数出现奇数次的数
这两个问题建立关系,将其前者转换为后者
回到题目还有一个条件没用:s
中的数是包含 1~n
,那么如果我把原来的数组后面再扩充 1~n
这时,原来是奇数次就变偶数次,原来是偶数次就变成了奇数次,不就转换为老问题了?
输入:nums = [1, 2, 2, 4] 1~n 中出现偶数次:2 3
扩充:nums = [1, 2, 2, 4, 1, 2, 3, 4] 1~n 中出现奇数次:2 3
数组实际不需要扩充,只需要多循环异或一次就好了
新瓶装旧酒
vector<int> findErrorNums(vector<int>& a) {
// eor = 缺失的数 ^ 重复的数
int eor = 0;
for (auto x : a) eor ^= x;
for (int i = 1; i <= a.size(); i++) eor ^= i;
// 分离
int rightOne = eor & (~eor + 1);
int one = 0;
for (auto x : a) {
if ((x & rightOne) != 0) one ^= x;
}
for (int i = 1; i <= a.size(); i++) {
if ((i & rightOne) != 0) one ^= i;
}
int two = eor ^ one;
// 分辨
for (auto x: a) {
if (x == two) swap(one, two);
}
return {one, two};
}