**LeetCode137. Single Number II_M

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 表示输出位,则自己定义的运算的真值表如下图:
image
则 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值