查找数组中出现奇数次的 2 个数(哈希法和异或法)

博客讲述了两种在数组中找出两个出现奇数次的数字的方法。第一种是使用哈希表,通过翻转出现过的数字的计数来找到奇数次出现的数字。第二种方法是利用异或运算,先对数组整体进行异或,然后通过找到一个二进制位的差异,将数组分为两部分,分别在两部分中再次进行异或操作,找出奇数次出现的数字。
摘要由CSDN通过智能技术生成

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))
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wohu007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值