剑指Offer 56I - 数组中数字出现的次数
题目描述
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
思路:位运算
- 异或运算复习
功能:相同为0,不同为1
运用:与0异或不变,与1异或取反
根据异或的性质很容易得出,假设数组中只有一个数字只出现了一次,那么数组中所有数字异或的结果,就是这个“落单”的数字。
因此对于这道题目,如果能设法把数组分成两部分,使得:
- 落单的两个数分别在不同的组中
- 相同的两个数分到同一组
那么对两个组分别异或,就可以得到答案。
接下来的问题是找到这样的分组标准。假设数组所有元素异或的结果为X,落单的两个数为A和B,则X就是A与B异或的结果。假设 Xi 代表X二进制表示的第i位,则当 Xi 为1时,根据数字二进制表示的第i位为0或为1,可以得到满足上述两个条件的两个分组,原因如下:
- 对于成对出现的数 - 第i位必然也相同,因此会被同时分到“1组”或“0组”
- 对于落单的A和B - 由于X是A与B异或的结果,则 Xi 也是 Ai 和 Bi 异或的结果。 Xi = 1,所以 Ai != Bi , 因此根据第i位的值来分组必然使得A与B被分到两个组。
步骤:
- 遍历数组,全部异或,得到X
- 初始化bitOne = 1,若X&bitOne为0,将bitOne右移一位,重复此步直到X&bitOne为1
- 初始化 a,b = 0,遍历一遍数组,如果第bitOne位为1,和a异或;为0,和b异或。
- a,b为最终结果。
代码:
/** * @param {number[]} nums * @return {number[]} */ var singleNumbers = function(nums) { let X = 0; for(let num of nums){ X ^= num; } let bitOne = 1; while(!(bitOne&X)){ bitOne <<= 1; } let a = 0, b = 0; for(let num of nums){ if(bitOne & num){ a ^= num; }else{ b ^= num; } } return [a,b]; };
时间复杂度:O(N)
空间复杂度:O(1)
PS: 得到只有第N位为1的二进制数 —— 将 1 左移N位

浙公网安备 33010602011771号