第一次写博客,有点小激动!
写这道题,主要是自己太菜,怕自己忘了。。。
原题
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
大佬的解题思路:
如果能设计一个状态转换电路,使得一个数出现3次时能自动抵消为0,最后剩下的就是只出现1次的数。
这里我找了一种较易理解的代码
public int singleNumber(int[] nums) {
int one = 0, two = 0, three;
for (int num : nums) {
// two的相应的位等于1,表示该位出现2次
two |= (one & num);
// one的相应的位等于1,表示该位出现1次
one ^= num;
// three的相应的位等于1,表示该位出现3次
three = (one & two);
// 如果相应的位出现3次,则该位重置为0
two &= ~three;
one &= ~three;
}
return one;
}
分析
1.这里的one、two、three的意思应该是:假如是(1,1,1)
当进入第一1的时候,one就为1,表示出现了一次
再进入一个1的时候,two也为1了,表示出现了两次
进入第三个1的时候,one、two都为1了,把three置为1表示出现了三次,这时候用~three做与运算,就是清零
这里把one、two、three都作为一种状态看
2.再看一下one、two、three本身
从二进制上用理解就000很容易了,还是以上面的例子看
当进入第一个1的时候,one=0001
进入第二个1的时候,one=0000,two=0001
进入第三个1的时候,one=two=0001,这时候three=0001,再~three和one、two做与运算,就置零了
(当然,这里的位数并不只是四位,与位数并不相关)
然后仔细一想,就发现,其实这个算法是在对每一个位进行运算,意思就是说每次进入一个数字的时候,
它二进制为1的部分就会放到one里面,如果one里面对应位置已经为1,则在two里面该位置1,表明该位
的1出现了两次,若one、two里面该位的值都为1,则three里面该位置1,然后就把该位置的1全部置零。
所以上面这种做法其实是对二进制上的每一位都进行了一种运算,只要该位出现过三次1,就会置0。所以无论
读入的数据怎样都无所谓,反正只要二进制上对应位的1出现过三次就置零。最后one里剩下就是只出现过
一次的数字的二进制信息。
理解:这里的实质应该是位运算,统计的是位上面的数字出现次数。可以联想,要是出现统计其他次数的,其实
也可以用这种对位运算的思想,而上面的代码具体实现就成了一种手段。
只能理解到这一步了,还得感谢一个朋友的帮忙。继续加油!!!