一、需求
- 在一个数组
nums
中除一个数字只出现一次之外,其他数字都出现了三次; - 请找出那个只出现一次的数字。
示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1
二、哈希表
2.1 思路分析
- 遍历数组,利用哈希表存储数组中每个元素以及各自出现的次数,最后返回次数为1的元素即可;
2.2 代码实现
class Solution {
public int singleNumber(int[] nums) {
Map<Integer,Integer> hm = new HashMap<>();
for(int i = 0; i < nums.length; i++) {
if(hm.containsKey(nums[i])) {
hm.put(nums[i], hm.get(nums[i])+1);
} else {
hm.put(nums[i], 1);
}
}
for(Integer key : hm.keySet()) {
if(hm.get(key) == 1) return key;
}
return -1;
}
}
2.3 复杂度分析
- 时间复杂度为O(N);
- 空间复杂度为O(N),哈希表存储数组元素占用O(N)的空间;
三、位运算法
3.1 思路分析
- 题目规定
,那么num[i]它的二进制位数最多为 32 位,我们建立一个counts数组来存在数组中所有元素其二进制位(从低位到高位)为1的个数,比如[3,4,3,3],3的二进制为...0011,4的二进制为...0100,那么该数组对应的counts数组中的值是[3,3,1,0,0,0...],即最低位1出现了3次,次低位1出现了3次,次次低位1出现了1次,其余位没有出现1,均为0;
- 通过1发现,只要当前counts数组对3取余即可得到[0,0,1,0,0,0...],这就是原数组中只出现1次的那个元素的二进制表示;
- 现在要将该二进制还原成原数字,可通过
来计算;
3.2 代码实现
class Solution {
public int singleNumber(int[] nums) {
int[] counts = new int[32];
for(int num : nums) {
//将每个数的二进制位累加到counts数组中
for(int j = 0; j < counts.length; j++) {
counts[j] += num & 1;
num = num >> 1;
}
}
//获得不重复数字的二进制位
for(int i = 0; i < counts.length; i++) {
counts[i] = counts[i] % 3;
}
//将二进制位转换成整数返回
int res = 0;
int tmp = 1;//看作2^0
for(int i = 0; i < counts.length; i++) {
res += tmp * counts[i];
tmp <<= 1;
}
return res;
}
}
3.2 代码优化
class Solution {
public int singleNumber(int[] nums) {
int[] counts = new int[32];
for(int num : nums) {
for(int j = 0; j < counts.length; j++) {
//当前最低位为1或0,是1就累加当前位1的个数
counts[j] += num & 1;
//将次低位移动到最低位
num >>>= 1;
}
}
//根据二进制位恢复只出现一次的数字
int tmp = 1;
int res = 0;
for(int i = 0; i < counts.length; i++) {
res += counts[i] % 3 *tmp;
tmp <<= 1;
}
return res;
}
}
3.3 复杂度分析
- 时间复杂度为O(N),其中N为数组元素的个数,每个数组元素进行位运算消耗的时间复杂度为O(1),故总体时间复杂度为O(N);
- 空间复杂度为O(1),数组counts和几个变量占用常数大小的额外空间;
四、学习地址
作者:Krahets