小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
介绍
解
这个题与上一题的区别仅在于将除答案外的元素出现次数从两次变成了三次,所以直接使用哈希表也是可以做的,但我们这里要探究位运算的解法。
在上一题中,我们根据位运算的一些性质可以得出结论:将所有元素都异或起来,最终答案就是只出现一次的数字。但是在这里情况不一样,元素的出现次数是三次,三个一样的数异或在一起还是它本身,所以上一种方法肯定不管用了。
对于这种题,实在思考不出来我喜欢写几个举例子来找找规律,结果还真给我找着了。我这里以 [2,2,3,2] 为例给大家看看我找到的规律,首先把它们都转为二进制(在位运算中,将数都转换为二进制再来思考解法是很重要的),然后以一个数组(也就是图中的 cnt[])来记录每一位 1 出现的次数。既然每个元素都只出现3次,那我把这个 1 出现的次数 模 3 剩下的岂不是就是那个没出现3次的数字,也就是答案了吗?看看下图可能会更容易理解。
想到这里,代码也就不是什么问题了。实现细节上,cnt数组只需要大小为32就行了,因为题目中给的数字大小范围是在32位以内的。
class Solution {
public int singleNumber(int[] nums) {
int[] cnt = new int[32]; // 维护一个记录 **二进制下每一位上1出现次数** 的数组
for (int num : nums)
for (int i = 0; i < 32; i++)
if (((num >> i) & 1) == 1)
cnt[i]++;
int ans = 0;
for (int i = 0; i < 32; i++)
if ((cnt[i] % 3 & 1) == 1)
ans += 1 << i;
return ans;
}
}
结论
这道题的解法也是非常巧妙,关键就在于把数都转为二进制,再在二进制下来思考如何找到解,画一画图找一找规律都是很有必要的。
这道题把这个元素出现次数改成4、5、6等等次数都是一样的做法,只要在每一位上去对相应的数取余就可以了,是不是很简单呢!当然在次数是偶数的时候可以使用上一篇的做法,毕竟两两异或后就抵消掉了(为0)。