1. 问题
数组中有 N+2 个数,其中, N 个数出现了偶数次,2 个数出现了奇数次(这两个数不相等),使用 O(1) 的空间复杂度,找出这个数。不需要知道具体位置,只需要找到这两个数即可。
2. 思路实现
2.1 hash 法
使用 hash 法,将数组中值作为 hash 的 key 来存储,如果该 key 对应的 value 为 0 , 说明之前没有出现过,那么将其置为 1,如果不为 0, 说明之前遇到过,那么将其反转置为 1,最后值结果为 1 的 key 就是要找的数字。
package main
import "fmt"
func findNumber(a []int) []int {
resultSlice := make([]int, 0)
d := make(map[int]int)
for i := 0; i < len(a); i++ {
if d[a[i]] == 0 {
d[a[i]] = 1
} else {
d[a[i]] = 0
}
}
for k, v := range d {
if v == 1 {
resultSlice = append(resultSlice, k)
}
}
return resultSlice
}
func main() {
a := []int{1, 3, 2, 6, 5, 7, 8, 7, 2, 1}
fmt.Println(findNumber(a))
}
2.2 异或法
遍历整个数组,依次做异或运算。由于数组存在两个出现奇数次的整数,所以最终异或的结果,等同于这两个整数的异或结果。这个结果中,至少会有一个二进制位是1(如果都是0,说明两个数相等,和题目不符)。
举个例子,如果最终异或的结果是 5,转换成二进制是 00000101。此时我们可以选择任意一个是 1 的二进制位来分析,比如末位。把两个奇数次出现的整数命名为 A 和 B,如果末位是 1,说明 A 和 B转为二进制的末位不同,必定其中一个整数的末位是 1,另一个整数的末位是 0。
根据这个结论,我们可以把原数组按照二进制的末位不同,分成两部分,一部分的末位是 1,一部分的末位是 0。由于 A 和 B 的末位不同,所以 A 在其中一部分,B 在其中一部分,绝不会出现 A 和 B 在同一部分,另一部分没有的情况。
这样一来就简单了,我们的问题就和 https://blog.csdn.net/wohu1104/article/details/124136103 中第二种方法一样了,按照原先的异或解法,从每一部分中找出唯一的奇数次整数即可。
package main
import "fmt"
func findNumber(arr []int) []int {
ret := make([]int, 0)
result := 0
for i := 0; i < len(arr); i++ {
result ^= arr[i] //得到a^b
}
a := 0
lastOneBit := result & (^result + 1) // 取二进制从左到右,最右边的一位的 1,
for i := 0; i < len(arr); i++ {
if lastOneBit&arr[i] == 0 { // 将原始数组分为两部分,只对其中一部分进行位运算
a ^= arr[i]
}
}
b := result ^ a // result == a^b; a == a^b^a == (a^a^b) == b
ret = append(ret, a, b)
return ret
}
func main() {
arr := []int{2, 2, 12, 12, 45, 85, 45, 45}
fmt.Println(findNumber(arr))
}