一.题目描述
在一个数组 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
二.题目解析
1.hashmap
public int singleNumber(int[] nums) {
/*hashmap法,时间复杂度O(n),空间复杂度O(n)
* */
int res = -1;
if(nums == null || nums.length == 0){
return res;
}
Map<Integer,Integer> map = new HashMap<>();
for (int cur:
nums) {
map.put(cur,map.getOrDefault(cur,0) + 1);
}
for (Integer i:
map.keySet()) {
Integer val = map.get(i);
if(val == 1){
res = i;
break;
}
}
return res;
}
2.位运算
不同,异或运算不能解决这个问题
上述思路不能解决这里的问题,因为三个相同的数字的异或结果还是该数字。尽管我们这里不能应用异或运算,我们还是可以沿用位运算的思路。
如果一个数字出现三次,那么它的二进制表示的每一位(0或者1)也出现三次。如果把所有出现三次的数字的二进制表示的每一位都分别加起来,那么每一位的和都能被3整除。如果某一位的和能被3整除,那么那个只出现一次的数字二进制表示中对应的那一位是0;否则就是1;
public int singleNumber2(int[] nums) {
/*位运算,时间复杂度O(n),空间复杂度O(1)
* */
int res = -1;
if(nums == null || nums.length == 0){
return res;
}
//java int类型有32位
int[] count = new int[32];
for (int i = 0; i < nums.length; i++) {
//每计算一个数需要重新初始化
int mask = 1;
for (int j = 0; j < 32; j++) {
//说明nums[i]的第J位值是1
if((nums[i] & mask) != 0){
count[j]++;
}
//mask不断左移直到nums[i]的每一位都被统计过
mask <<= 1;
}
}
//还原各位值为10进制表示
res = 0;
// int bitValue = 1;
//以最高位为例,需要左移31次
//i = 0,0次-》i=1,1次-》....i=31,31次
for (int i = 0; i < count.length; i++) {
//顺序不能颠倒,否则最后一步会多左移一次
res <<= 1;
res += count[31-i] % 3;
}
return res;
}
总结:数组中一个数字出现一次,其他数字出现奇数次问题可用此方法解决(如果是偶数次,直接用异或运算解决)。