题目来源
题目描述
题目解析
位运算
如果直接异或运算的话,得到的数则为两个目标数的异或值,那应该怎么区分出这两个值呢?
我们试想一下,如果我们先将元素分成两组,然后每组包含一个目标值,那么异或之后,每组得到一个目标值,就可以将两个目标值分别求出了。比如:
- 例: **a,b,a,b,c,d,e,f,e,f ** 分组后
- A组:a, a , b, b, c 异或得到 c
- B组:e, e, f, f, d 异或得到 d
那我们应该依据什么规则将其进行分类呢?
- c、d是两个不同的数,那么二进制位上一定会有一位是不同的,那么我们就可以根据这一位(分组位)来将 c , d 分到两个组中,数组中的其他元素,要么在 A 组中,要么在 B 组中。
那我们应该怎么得到分组位呢?
- 我们让c、d异或即可,异或运算就是对应位不同时得 1 ,异或之后值为 1 的其中一位则为我们分组。
- 例 001 ⊕ 100 = 101,我们可以用最右边的 1 或最左边的 1 做为分组位,数组元素中,如果我们将最右边的1作为我们的分组位,最后一位为0则进入A组,为1则进入 B 组。
那么我们应该怎么借助分组位进行分组呢?
- 我们处理c、d的异或值,可以仅仅保留异或值的分组位,其余位变为0,比如101 变成 001或 100
- 为什么要这么做呢?我们可以根据 a & 1 来判断 a 的最后一位为 0 还是为 1,所以我们将 101 变成 001 之后,然后数组内的元素 x & 001 即可对 x 进行分组 。同样也可以 x & 100 进行分组.
那么我们如何才能仅保留分组位,其余位变为 0 呢?例 101 变为 001
我们可以利用 x & (-x) 来保留最右边的 1
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
// 一对相同的数字异或为0
// 所以target为两个只出现了一次的元素的异或
int xorsum = 0;
for(auto num : nums){
xorsum ^= num;
}
// 因为两数不同,lowbit必然不为0
// 物理意义就是两个不同的数字不同的最低的位在哪
int lowbit = xorsum == INT_MIN ? xorsum : xorsum & (-xorsum);
int a1 = 0;
int a2 = 0;
// 重复上述的过程,但是将nums按照lowbit为1或者为0分类
// 则两个数必然被分到不同的类目;而相同的数字一定在同一个类目
// 所以按类目分别异或就可以得到两个不同的数字
for(auto n : nums){
if(n & lowbit){
a1 ^= n;
}else{
a2 ^= n;
}
}
return {a1, a2};
}
};
类似题目
题目 | 思路 |
---|---|
leetcode:41. 无序数组中缺失的第一个正整数(数组数据范围[-oo, +oo]) First Missing Positive | 应该去垃圾区的情况:当前数 <= L ,当前数 > 右边界 ,出现了重复 |
leetcode:268. 无序数组缺失的那个数(数组数据范围在[0, n],数据长度为n) Missing Number | 如果我们补充一个完整的数组和原数组进行组合,那所求解的问题就变成了 只出现一次的数字。 |
leetcode:448. 无序数组缺失的那个数(数组数据范围在[1, n],数据长度为n) Find All Numbers Disappeared in an Array | 标记法:在原数组上进行一次遍历,在遍历的过程中,将遇到的元素所对应的index下的元素标记为负。最终哪些index下的元素没有被标记的则说明,这个index对应额元素没有出现过。置换法: |
leetcode:136. 无序数组出现一次的数字(出现一次的数字有一个,其他出现两次)Single Number | 异或相消,剩下来的就是想要的 |
leetcode:137. 无序数组出现一次的数字(出现一次的数字有一个,其他出现三次) II Single Number IISingle Number II | 对于出现三次的数字,各个二进制位出现的次数都是3的倍数。因此,统计所有数字的各二进制位中1的出现次数,并对3求余,结果则为只出现一次的数字(通用解法)。实现:对int的32个位每个位进行依次判断该位1的个数求余3后是否为1,如果为1说明结果该位二进制为1可以将结果加上去。最终得到的值即为答案。 |
leetcode:260. 无序数组出现一次的数字(出现一次的数字有两个,其余出现两次)Single Number III | 先将元素分成两组,然后每组包含一个目标值,那么异或之后,每组得到一个目标值。比如a,b,a,b,c,d,e,f,e,f,分组后;A组:a, a , b, b, c 异或得到 c;B组:e, e, f, f, d 异或得到 d |
leetcode:287.无序数组出现多次的数字(一个数出现多次,其他数出现一次或者零次) (数组数据范围在[1, n],数据长度为n+1) Find the Duplicate Number | 因为不允许修改原数组,所以不能用标记法。而一般对于链表,数组中要求找重复的数字或者节点,都可以考虑用快慢指针的方法来解,也就是入环节点就是那个重复的数 |
leetcode:442.无序数组出现两次的整数(每个数出现一次或者两次,数组数据范围在[1, n])) Find All Duplicates in an Array | 标记法:对于每个nums[i],我们将其对应的nums[nums[i] - 1]取相反数,如果其已经是负数了,说明之前存在过,我们将其加入结果res中即可;置换法: 将nums[i]置换到其对应的位置nums[nums[i]-1]上去,最后在对应位置检验,如果nums[i]和i+1不等,那么我们将nums[i]存入结果res中即可 |
leetcode:645. 无序数组,有一个数字重复出现了一次,从而造成了另一个数字的缺失,请找出重复的数字和缺失的数字(数据范围在[1, n],数据长度为n) Set Mismatch | 假设每个人各司其职, 那么每个岗位上就是相应的人员,并且各司其职, 但是现在混入了一个;闲杂人等, 他又害怕被人发现, 于是他就别人的岗位上站着, 结果等着等着, 这个岗位上应该来的人来了;他一看, 自己掩饰不过去了(毕竟人家不会跑到别的岗位上,抢别人的活干, 于是他就这么一直碰运气。 一直走到那个没来上岗的那个人的岗位上去, 这样谁都不会发现他是的闲杂人等.而且每个岗位上都满员了. |
leetcode:142. 环形链表 II | |
leetcode:540. 有序数组中的单一元素(其他出现两次) | 二分 |
leetcode:389. 字符t比字符s多出的那个数 Find the Difference | 异或 |
765. 情侣牵手Couples Holding Hands | |
565. 数组嵌套 Array Nesting | |
1207. 独一无二的出现次数 | 先统计数字出现的频率,然后看相同频率是否重复出现 |