题目描述
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
示例 1:
输入:nums = [2,2,3,2]
输出:3
提示:
1 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次
解题思路
今天先放动态规划一码!做做《每日一题》
一开始,完全没有思路。休息之后再战,想的是“”除某个元素仅出现 一次 外,其余每个元素都恰出现 三次”这句话肯定富含深意,打算从这里入手。因为之前有题目是通过存储a-z共26个字母来解析存储字符串的分布特征的,所以直接想到,能不能把出现的数字作为下标。下个时刻就否决了,因为数字太大了,这得浪费多少空间。然后想,怎么样能够用暴力破解?通过二维数组来记录每个数字出现的次数,但发现也不好实现。
再过了段时间,我看到下方的进阶要求,于是想,利用额外空间?哦,HashMap可以!每个key就是num[i],value就是出现的次数。于是
public int singleNumber(int[] nums) {
// 本题思前想后,也只能在题目的进阶那里得到灵感,用hashMap
int size = nums.length / 3 + 1;
Map<Integer, Integer> numMap = new HashMap<>(size);
// 统计每个数字出现的次数
for (int i=0; i<nums.length;i++) {
numMap.put(nums[i], numMap.getOrDefault(nums[i], 0) + 1);
}
// 找到出现次数为1的
for (Map.Entry<Integer, Integer> entry: numMap.entrySet()) {
if (entry.getValue() == 1) {
return entry.getKey();
}
}
return 0;
}
但是,是在想不到还是什么其他的更好的方式。看了一眼官方解答,不服不行。。。甚至,到最后面的电路设计解题思路,都直接看不懂,那个根据真值表设计电路的表达式。。。慢慢来吧。第二种解法倒是看懂了。
进阶方案一
每个数字都是int能够表示的,那么就有32bit,所以只要将每个数字的每一位单独加和,然后对3求余。再将余数通过或运算组合成新的32bit,那么就是所求答案。
进阶方案二
这个有点复杂,在方案一中通过整数的每个位置上的数字来解题,那么是否存在位运算可以达到同样的目的呢?
由于1位二进制,无法表示题目中数字出现3次——3种状态,于是想到用2位。00表示出现1次,01表示出现2次,10表示出现了3次。那么对于一个数字而言,必然是00->01->10依次出现,
黑盒中存储了两个整数 aa 和 bb,且会有三种情况:
a 的第 i 位为 0 且 b 的第 i位为 0,表示 0(可以理解为对3求余);
a 的第 i 位为 0 且 b 的第 i 位为 1,表示 1(可以理解为对3求余);
a 的第 i 位为 1 且 b 的第 i 位为 0,表示 2(可以理解为对3求余)。
如果我们用这种状态表示一个数字的二进制位Xi的出现情况,假设有Ai和Bi,则有如下表
AiBi | Xi | 新AiBi |
---|---|---|
00 | 0 | 00 |
00 | 1 | 01 |
01 | 0 | 01 |
01 | 1 | 10 |
10 | 0 | 10 |
10 | 1 | 00 |
可能有些同学还看不懂,这里的意思是,我有2个数字,一个是A,一个是B。还有另外一个数字X,这个X就是我们题目数组中的一个元素。而i表示的是二进制位上的第i位。
而关系表达式 :
Ai=Ai’BiXi + AiBi’Xi’
意思是 Ai’&Bi&Xi 或 Ai&Bi’&Xi’
最后,我们求的是只出现一次的数字,对应会我们的状态(可以理解为对3求余)切换 00(0)-01(1)-10(2),那么就是Bi,所以最后返回B。
今天先到这吧,有点烧脑,不然晚上太兴奋会睡不着的。