题目描述:
给你一个整数数组 nums ,除某个元素仅出现一次外,其余每个元素都恰出现三次。请你找出并返回那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法且不使用额外空间来解决此问题。
示例 1:
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,99]
输出:99
提示:
1 <= nums.length <= 3 * 10^4
-2^31 <= nums[i] <= 2^31 - 1
nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次。
这个问题在考虑了好几天之后,还是没有思路:
初始解法:
class Solution {
public int singleNumber(int[] nums) {
Map<Integer,Integer> map = new HashMap<>();
for(int i = 0; i < nums.length ; i++){
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
}
for(Map.Entry<Integer,Integer> entry: map.entrySet()){
if(entry.getValue() == 1){
return entry.getKey();
}
}
return 0;
}
}
这种解法很好想到,主要是空间复杂度为O(n)
那么空间复杂度为O(1)的算法是怎样的呢?
此处未参考电路的做法(主要是电路的做法没有看明白),使用了遍历统计的方法:
class Solution {
public int singleNumber(int[] nums) {
// 假如例子是 1 2 6 1 1 2 2 3 3 3,其中存在 3 个 1, 3 个 2, 3 个 3,1 个 6
// 1: 0 0 1
// 2: 0 1 0
// 6: 1 1 0
// 1: 0 0 1
// 1: 0 0 1
// 2: 0 1 0
// 2: 0 1 0
// 3: 0 1 1
// 3: 0 1 1
// 3: 0 1 1
// 看最右边的一列 1001100111 有 6 个 1
// 再往前看一列 0110011111 有 7 个 1
// 再往前看一列 0010000 有 1 个 1
// 我们只需要把是 3 的倍数的对应列写 0,不是 3 的倍数的对应列写 1
// 也就是 1 1 0,也就是 6。
//下面是数据统计类思路:
//由于限制了数据的大小,所以
//1.依次找到每个数字的第i位二进制,然后将二进制位数相加,最后得到的结果/3,再来计算最后结果
int ans = 0 ;
for(int i = 0; i < 32; i++){
int count = 0;
for(int j =0; j< nums.length; j++){
//这里的意思是右移这些位,得到的数字跟1&,如果得到的结果为1,那么说明当前数字的当前二进制位上是1
if(((nums[j] >>> i) & 1) == 1){
count++; //得到出现1的个数
}
}
//得到不是3的倍数
if(count %3 != 0){
//注意这里,不是很好理解,为什么需要将1左移,因为需要将最后的结果输出出来,那么发现第几位不是3的倍数,就需要左移多少位,同时那个位置放置1
ans = ans | ( 1 << i);
}
}
return ans;
}
}
此算法没有理解,存靠硬套答案。
问题升级:只出现一次的数字③