137.Single Number II_M
Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
方法一:用两个 HashSet
当第一个 set 里面没有这个数时,添加进去;
当第一个 set 里面有这个数,第二个 set 里面没有这个数时,添加进第二个 set;
当两个 set 里面都有这个数时,删除两个 set 里面的这个数;
最后第一个 set 里面剩下的唯一的数就是我们要找的只出现一次的那个数;
注意:最后int result = singleNum.iterator().next();因为 Set 继承了Collection 接口,Collection 实现了Iterator 接口,所以可以使用 HashSet 的迭代器返回最后一个数。
public int singleNumber(int[] nums) {
Set<Integer> singleNum = new HashSet<>();
Set<Integer> singleNum1 = new HashSet<>();
for(int i = 0; i < nums.length; i++){
if(singleNum.contains(nums[i])){
if(singleNum1.contains(nums[i])){
singleNum.remove(nums[i]);
singleNum1.remove(nums[i]);
}
else{
singleNum1.add(nums[i]);
}
}
else{
singleNum.add(nums[i]);
}
}
int result = singleNum.iterator().next();
return result;
}
方法二:用 Map
Map 中的 key 为数组中的值,value 为数组中的数出现的次数。
注意:Map 没有实现 Iterator 接口,所以 Map 没有迭代器方法,最后 int result = singleNum.keySet().iterator().next(); 最后将 Map 的 key 转换成 keyset 再使用迭代器返回最后一个数。
public int singleNumber4(int[] nums) {
Map<Integer, Integer> singleNum = new HashMap<>();
for(int i = 0; i < nums.length; i++){
if(singleNum.containsKey(nums[i])){
if(singleNum.get(nums[i]).intValue() == 1){
singleNum.remove(nums[i]);
singleNum.put(nums[i], 2);
}
else if(singleNum.get(nums[i]).intValue() == 2){
singleNum.remove(nums[i]);
}
}else{
singleNum.put(nums[i], 1);
}
}
int result = singleNum.keySet().iterator().next();
return result;
}
方法三:位运算 – 能返回出现 1 次和出现 2 次的数
总结: 一个数组中有一个元素只出现 1 次,其余元素出现 k 次
当 k 为偶数时,用异或的方法;
当 k 为奇数时,用位运算,数组中的每个元素的每一位相加再模 k ,结果就是只出现一次的元素。对于本题,模 3 有 3 种状态,余数为0,1,2, 所以用两位来表示00,01,10。
定义一种位相加运算规则:
00 + 1 = 01; 01 + 1 = 10; 10 + 1 = 00;
用 ab 表示当前位的状态,c 表示输入数字的当前位,next ab 表示输出位,则自己定义的运算的真值表如下图:
则 a = ( ~ a & b & c) | (a &~ b &~ c);
b=( ~ a & ~ b & c) | (~ a & b & ~ c);
a 位为 1 时,代表数只出现了两次;
b 位为 1 时,代表数只出现了一次;
所以返回 a 代表返回出现了两次的数;返回 b 代表返回出现了一次的数;返回 a|b 代表返回出现了一次或者两次的数。
public int singleNumber3(int[] nums) {
//we need to implement a tree-time counter(base 3) that if a bit appears three time ,it will be zero.
//#curent income ouput
//# ab c/c ab/ab
//# 00 1/0 01/00
//# 01 1/0 10/01
//# 10 1/0 00/10
// a=~abc+a~b~c;
// b=~a~bc+~ab~c;
int a=0;
int b=0;
for(int c:nums){
int ta=(~a&b&c)|(a&~b&~c);
b=(~a&~b&c)|(~a&b&~c);
a=ta;
}
//we need find the number that is 01,10 => 1, 00 => 0.
return a|b;
}
方法三:位运算
方法思路和上面一样,在真值表中找运算规则得到的运算公式不一样而已。
这里 one 为 1 代表出现一次;
two 为 1 代表出现两次;
public int singleNumber2(int[] nums) {
int ones = 0, twos = 0;
for(int i = 0; i < nums.length; i++){
ones = (ones ^ nums[i]) & ~twos;
twos = (twos ^ nums[i]) & ~ones;
}
return ones;
}