题目:(137. 只出现一次的数字 II)
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。
请你找出并返回那个只出现了一次的元素。
----------
示例:
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,100]
输出:100
提示:
1 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次
-------------
思考:
遍历统计
用 与运算 ,可获取二进制数字 num的最右一位n1:n1=num&i
配合 无符号右移操作 ,可获取 num所有位的值(即n1~n32):num=num>>>1
建立一个长度为 32 的数组 counts ,通过以上方法可记录所有数字的各二进制位的 1 的出现次数
int[] counts = new int[32];
for(int i = 0; i < nums.length; i++) {
for(int j = 0; j < 32; j++) {
counts[j] += nums[i] & 1; // 更新第 j 位
nums[i] >>>= 1; // 第 j 位 --> 第 j + 1 位
}
}
将 counts各元素对 3 求余,则结果为 “只出现一次的数字” 的各二进制位。
for(int i = 0; i < 32; i++) {
counts[i] %= 3; // 得到 只出现一次的数字 的第 (31 - i) 位
}
利用 左移操作 和 或运算 ,可将 counts数组中各二进位的值恢复到数字 res上
(循环区间是i∈[0,31] )。
for(int i = 0; i < counts.length; i++) {
res <<= 1; // 左移 1 位
res |= counts[31 - i]; // 恢复第 i 位的值到 res
}
最终返回 res即可。
复杂度分析:
时间复杂度 O(N) : 其中 N 位数组 nums的长度;遍历数组占用 O(N) ,
每轮中的常数个位运算操作占用 O(1)。
空间复杂度 O(1): 数组 counts长度恒为 32 ,占用常数大小的额外空间。
实际上,只需要修改求余数值 m ,即可实现解决 除了一个数字以外,其余数字都出现 m 次 的通用问题。
------------------
class Solution {
public int singleNumber(int[] nums) {
int[] counts = new int[32];//记录所有数字的各二进制位的 1 的出现次数
for (int num : nums) {//! 遍历数组中元素
for (int j = 0; j < 32; j++) {//每个元素
counts[j] += num & 1; //用 与运算 ,可获取二进制数字 num的最右一位 //!
num >>>= 1; //配合 无符号右移操作 ,可获取 num所有位的值
}
}
int res = 0;
int m = 3;
//将 counts各元素对 3 求余,则结果为 “只出现一次的数字” 的各二进制位
for (int i = 0; i < 32; i++) {
res <<= 1;// 左移 1 位
res |= counts[31 - i] % m;// 恢复第 i 位的值到 res
}
return res;
}
}
LC