题目
解题思路
预备知识:异或运算 和 &运算
1、异或运算:
因为 1^1=0 1^0=0 所以 4^4 = 100 ^ 100 = 000 即 a ^ a = 0
所以如果一组只有一个数出现一次的时候全员异或一遍即可得到这个特殊的数 eg:[3,1,3] = 3 ^ 1 ^ 3 = 1
2、&运算
1&1=1 1&0=0 0&0=0 所以 6 & 1 = 110 & 001 = 000 或 6 & 4 = 110 & 100 = 100
分组思路:
从上面知道"如果一组只有一个数出现一次的时候全员异或一遍即可得到这个特殊的数",那么组里有两个只出现一遍的数怎么办?
我们自然是想把它们进行分成两组A组和B组,然后A组全员异或得到特殊数a,B组全员异或得到特殊数b
而这两组自然要满足如下条件:(此处以[1,2,7,4,1,4]数组作为例子)
- ① 这两个特殊的数分别在A组、B组各有一个
- ② 其他N对的相同的元素一定要一对都在同一个组(即不会出现一个4在A组 一个4在B组,这样无法在一组内通过异或来抵消了)
如何分组:
这时候关键就是如何分组,我们暂时先来明确这以下一些关键点:
- 两个不同的数进行异或运算后一定至少有一个位是为1的(否则全部为0代表是相同的数字)
eg:2^7 = 010 ^ 111 = 101 - 通过逐一与mask变量进行&运算区分为两组:
mask变量怎么得到?
我们声明一个mask变量,这个变量满足的要求就是除了两个特殊数的异或结果的"那一位不为1的位置"是1以外别的都是0
所以这个例子中mask可以001(1)或者100(4),这里假设选择001
mask变量是怎么将一个数组区分为两组的?
- 怎么满足条件①?
例如依旧是这个例子:nums为[1,2,7,4,1,4],我们上面2.1知道mask是怎么取得,我们取第一个mask=001
分别将mask与两个特殊数2 、7进行&运算:mask & 2 = 001 & 010 = 000(等于0) 而 mask & 7 = 001 & 111 = 001 (不等于0 即!= 0)
然后我们可以知道mask与两个特殊数字进行&运算一定有一个是等于0一个是不等于0的,由此把两个特殊的数字分成两组,满足条件① - 最后我们怎么满足条件②呢?
其实很简单也是mask逐一与每个num进行&运算,如果mask&num==0的肯定是分到同一组 mask&num!=0自然而然的也是分到另外一组。
代码:
func singleNumbers(nums []int) []int {
//1、先全员遍历一遍得到特殊的两个数的异或结果ret
ret := 0
for _, num := range nums{
ret ^= num
}
//2.1获得mask
//先假设ret的第一位为1,所以初始mask为001
mask := 1
for ret & mask == 0{ //说明假设ret的第N位并不为1,再将mask左移1位,检验ret的N+1位是否为1
mask = mask << 1
}
//2.2区分两组进行全员异或得到两个特殊数
//先初始化两个特殊数
a, b := 0, 0
for _, num := range nums{
if num & mask == 0{
a ^= num
}else{
b ^= num
}
}
return []int{a, b}
}