剑指Offer_#56-II_ 数组中数字出现的次数II
Contents
题目
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1
限制:
1 <= nums.length <= 10000
1 <= nums[i] < 2^31
思路分析
思路是统计数字的二进制表示中每一位出现的次数。
- 对于出现3次的数字,每个二进制位也出现3次;多组出现3次的数字叠加,每个二进制位出现次数是3的倍数。
- 再考虑只出现一次的数字,即我们的目标数字。目标数字中,出现1的二进制位会增加到之前的统计次数中,使得这个位置出现1的总次数不再是3的倍数。
算法流程
- 遍历数组,对于每个元素,循环右移位,通过掩码1获取到二进制表示中每一位1出现的次数,保存到一个辅助数组中,索引表示第几位,元素表示出现的次数。
- 遍历刚才的辅助数组,看每个元素对3取余的结果
- 若为0说明这一位出现了3的整数倍次,必然没有在目标数字中出现过,所以不计算
- 若不为0,说明这位数字出现在目标数中,res加上digit,也就是当前二进制位的数量级(2n)
解答
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
//二进制中每一位数字的数量级
int digit = 1;
//统计二进制表示中每一位出现的总个数
int[] numTimes = new int[32];
for(int num:nums){
for(int i = 0; i <= 31;i++){
//如果第i位是1,那么help[i]增加1
numTimes[i] += (num & 1);
//继续统计下一位
num >>= 1;
}
}
//遍历help[]数组
for(int times:numTimes){
res += digit * (times % 3);
digit <<= 1;
}
return res;
}
}
复杂度分析
时间复杂度:O(n),两层循环中,第二层循环次数是常数,所以还是O(n)。
空间复杂度:O(1),借助一个辅助数组,其长度是常数。