**LeetCode137. Single Number II_M

来源:互联网 发布:java log4j使用实例 编辑:程序博客网 时间:2024/05/20 20:55

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;}
原创粉丝点击