Java 算法-落单的数II(位运算)

  今天在lintCode上面做了一道关于位运算的题,感觉有必要记录下来。

1.概览

(1).题意

给出3*n + 1 个的数字,除其中一个数字之外其他每个数字均出现三次,找到这个数字。

(2).样例

给出 [1,1,2,3,3,3,2,2,4,1] ,返回 4

(3).挑战

一次遍历,常数级的额外空间复杂度

2.第一种解法

  这个题我所知道的有两种解法(关于哈希的算法,这里不讲解)。
  我们知道,int类型的数据都是32位,所以我们这里定义32长度的数组,用来记录每一个数的二进制位的每一位上1出现的次数
  例如,一个数字10,32位二进制为00000000 00000000 00000000 00001010,假设数组为A[],那么只有A[1]为1,A[3]为1。如果有n个数字,A数组记录的是n个数字每一位出现的次数。
  如果一个数字出现3次,那么对应的位数出现的次数肯定为0,我们将它%3,如果剩下的数字不为0,表示只出现一次的那个数字该位肯定为1。

(1).代码

    public int singleNumberII(int[] A) {
        if (A == null || A.length == 0) {
            return 0;
        }
        int result = 0;
        int a[] = new int[32];
        for (int i = 0; i < 32; i++) {
            for (int j = 0; j < A.length; j++) {
                a[i] += A[j] >> i & 1;
            }
            a[i] %= 3;
            result = result | (a[i] << i);
        }
        return result;
    }

3.第二种解法

  相较于第一种解法,第二种解法时间复杂度更加的低,只用了一重循环。但是理解起来相对来说,比较复杂。
  这里我先贴代码,然后再解释意思。

(1).代码

    public int singleNumberII(int[] A) {
        if (A == null || A.length == 0) {
            return 0;
        }
        int one = 0;
        int two = 0;
        for (int i = 0; i < A.length; i++) {
            one = (one ^ A[i]) & (~two);
            two = (two ^ A[i]) & (~one);
        }
        return one;
    }

  这里先说一下总结:如果一个数字只出现一次,one肯定会保存;如果一个数字出现了两次,two会保存;如果一个数字出现三次,two和one都会被清零。
  这里举一个例子来说明,假设数组A[] = {1,1,1}。第一次变量,one为1,two为 0;第二次遍历,one为0,two为1,;第三次遍历,one为0,two为0。因此我们发现如果一个数字出现了三次,那么这个数字的会清零

你可能感兴趣的:(Java 算法-落单的数II(位运算))