一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是 O (n),空间复杂度是 O (1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
分组异或
写了里面那一题,这题就简单多了
有两个单身狗,那么只要先找第一个,然后再找第二个就行了
但是,如何分组呢?
4 ^ 1 ^ 4 ^ 6 => 1 ^ 66 对应的二进制: 1101 对应的二进制: 0011 ^ 6 二进制: 111
此时我们无法通过 111(二进制),去获得 110 和 001。
那么当我们可以把数组分为两组进行异或,那么就可以知道是哪两个数字不同了。
我们可以想一下如何分组:
重复的数字进行分组,很简单,只需要有一个统一的规则,就可以把相同的数字分到同一组了。例如:奇偶分组。因为重复的数字,数值都是一样的,所以一定会分到同一组!
此时的难点在于,对两个不同数字的分组。
此时我们要找到一个操作,让两个数字进行这个操作后,分为两组。
我们最容易想到的就是 & 1 操作, 当我们对奇偶分组时,容易地想到 & 1,即用于判断最后一位二进制是否为 1。来辨别奇偶。
你看,通过 & 运算来判断一位数字不同即可分为两组,那么我们随便两个不同的数字至少也有一位不同吧!
我们只需要找出那位不同的数字 mask,即可完成分组( & mask )操作。
由于两个数异或的结果就是两个数数位不同结果的直观表现,所以我们可以通过异或后的结果去找 mask!
所有的可行 mask 个数,都与异或后 1 的位数有关。
num1: 101110 110 1111num2: 111110 001 1001num1^num2: 010000 111 0110可行的mask: 010000 001 0010010 0100100
为了操作方便,我们只去找最低位的 mask:
/*** @param {number[]} nums* @return {number[]}*/var singleNumbers = function (nums) {let sum = 0;for (let num of nums) {sum ^= num;}//获得k中最低位的1let mask = 1;while ((sum & mask) === 0) {mask <<= 1;}//将不同的数拆分开,想象成在两个集合中继续异或,最终剩下的数就是不同的数let a = 0, b = 0;for (let i = 0; i < nums.length; i++) {if ((nums[i] & mask) === 0) {a ^= nums[i];} else {b ^= nums[i];}}return [a, b]}
