分组位运算-数组中数字出现的次数(Go语言)

题目

剑指offer56-I
在这里插入图片描述

解题思路

预备知识:异或运算 和 &运算
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. 两个不同的数进行异或运算后一定至少有一个位是为1的(否则全部为0代表是相同的数字)
    eg:2^7 = 010 ^ 111 = 101
  2. 通过逐一与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}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值