-
思路一
用一个哈希表记录数字出现的次数,然后再次遍历,返回次数为1的数字,唯一值得注意的就是对于map的getOrDefault方法的运用
时空复杂度:假设nums长度为n,那么哈希表长度为n/3+1,各遍历一次,所以时间复杂度为O(n),由于哈希表容量为n/3+1,所以空间复杂度为O(n)
public int singleNumber(int[] nums) { HashMap<Integer, Integer> numTimes = new HashMap<>(nums.length / 3); for (int num : nums) { numTimes.put(num, numTimes.getOrDefault(num, 0) + 1); } for (Map.Entry<Integer, Integer> entry : numTimes.entrySet()) { if (1 == entry.getValue()) return entry.getKey(); } return -1; }
-
思路二
首先我们用一个长度为32的int数组numTimes来存储数组中所有的数字的二进制下每一位的和,比如nums为[2,2,1,2] 那么int数组就为[0,0,0······3,1]
因为所有的数字都出现了三次,如果没有那个出现一次的数字,那么numTimes中的每一个数字都可以整除3,但是因为有一个出现了一次的数字,那么如果对于numTimes中的某一个数字n,如果可以整除3,那么对于出现一次的数字k在这一位一定是0,如果不能整除,那么k的这一位一定是1
时空复杂度:上方for循环为O(32n)可以看作O(n),下方for循环为O(1),所以整体时间复杂度为O(n),额外空间开辟了一个长度为32的int数组,可以看作O(1)
public int singleNumber(int[] nums) { int[] bitNums = new int[32]; int result = 0; //把数组按位存储到bitNum中 for (int num : nums) { //获取num每一位的值并填充 for (int i = 0; i < 32; i++) { bitNums[31 - i] += num & 1; num >>= 1; } } for (int bitNum : bitNums) { result = (result << 1) + bitNum % 3; } return result; }
需要解释一下这里
bitNums[31 - i] += num & 1;
我们在下面遍历数组的时候是从前往后遍历的,我们每遍历一次result都要左移一次,所以bitNum数组应该把高位放到低索引
补充
- 这个题目有一个简化版本:一个数只出现一次,其余数字出现两次,这时把所有数组元素异或一次即可
- 这个题目可以引申,即某个数字出现m次,其余数字都出现n次,都可以用此种做法,前提是m不能被n整除
- 注意一个区别,此前我们用i&i-1实现了把某个数字的最右侧的1变成0,但是这里不能用,因为我们要准确的知道某个数字的二进制下哪一位是1,哪一位是0
Go版本
- 思路一
func singleNumber(nums []int) int {
numMap := map[int]int{}
for _, num := range nums {
numMap[num]++
}
for k, v := range numMap {
if v == 1 {
return k
}
}
return -1
}
注意的是初始化map的方式和利用map计数时可以不判断是否存在,因为不存在会返回0,所以直接++即可
- 思路二
func singleNumber(nums []int) int {
result := int32(0)
for i := 0; i < 32; i++ {
total := 0
for _, num := range nums {
total += (num >> i) & 1
}
if 0 != total%3 {
result |= int32(1 << i)
}
}
return int(result)
}
注意这里在go需要用int32,否则符号位的解析会出现问题,再一个是这个思路,外层循环是第i位的情况,内存循环是在遍历每个数字,这样的话一个嵌套循环就可以了,不需要再用一个数组去记录每一位的情况,最后再去判断。下面的思路相较于思路二就比较麻烦了
func singleNumber(nums []int) int {
var bitArray [32]int
for _, num := range nums {
for i := 0; i < 32; i++ {
bitArray[i] += ((num >> i) & 1)
}
}
var result int32
for index, bit := range bitArray {
if bit % 3 != 0{
result |= int32(1 << index)
}
}
return int(result)
}