给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
参考一下大神的思路
https://juejin.im/post/5cb962dbf265da03761e8601
首先
一般看到这种题,我们第一反应是做状态记录,比如数字x出现了y次.然后从记录里查询出现了一次的数字.
但是 现在我们的空间复杂度被限制成了O(1),不可能对所有的64位二进制表示的整数进行统计次数.
于是
我们只能着眼于二进制上了.我们先将状态记录缩小到记录目标数字的每一个二进制上.
那么这题就会变成: 遍历所有数,一个二进制位出现1的次数对3取余是否为1,若为1,那么我们目标数字的这一位也就是1. (具体见下例)
比如
[1,1,1,2] 他们转换成二进制就是[01,01,01,10],我们可以知道第一位(从右往左)的二进制总共出现了三次1,那么我们的目标结果在第一位就绝对不会是1,相对的第二位出现了一次1,那么我们的目标值在这一位就是1
实际上
这题我们也可以写64个变量(不知道数据量范围多大或者32位也可以?)记录每位的二进制1出现的次数,然后每一个对三取余最后得出的64个1与0再组成一个整数…不过思路本质离不开这里.
而且到这一步空间复杂度已经是O(1)了,毕竟一个定长数组.但是大佬就是不一样,非要用几个变量解决…
最后
我们要运用逻辑门的想法设计一个计数器.计数到3就回归0.对于每一个二进制位,出现了多少次1我们可以用两个二进制表示(因为这题只需要记录%3),00表示出现0次,01表示出现1此,10表示出现2次.这样就记录了三个状态,(题目中是出现三次,其实对于x次,我们也可以同理描述).
public int singleNumber(int[] nums) {
int ones = 0;
int twos = 0;
int threes = 0;
for (int num:nums) {
twos |= ones & num;
ones ^= num;
threes = ones & twos;
ones &= ~threes;
twos &= ~threes;
}
return ones;
}