Single Number题解整理

来源:互联网 发布:java开发的网游 编辑:程序博客网 时间:2024/06/05 20:54

Q: Given an array of integers, every element appears three times except for one. Find that single one.

 

如果是其余元素均出现两次,这个题目很容易解决,我们直接可以用xor,把所有的数异或之后的结果,便是出现一次的数字。但是这个题目其余元素出现的次数是三次,如果数组中的元素都是三个三个出现的,那么从二进制表示的角度,每个位上的1加起来,应该可以整除3。

基本方法:

如果有一个数x只出现一次,会是什么情况呢?

    如果某个特定位上的1加起来,可以被3整除,说明对应x的那位是0,因为如果是1,不可能被3整除

    如果某个特定位上的1加起来,不可以被3整除,说明对应x的那位是1

根据上面的描述,我们可以开辟一个大小为32的数组,第0个元素表示,A中所有元素的二进制表示的最低位的和,依次类推。

int get_high_bit_one(int num){for (int i = 31; i >=0; --i){if (!(num & (1<<i)))continue;return i;}}int find_once_number(int arr[], int len){int* bit_arr = new int[32];int num = 0;if (bit_arr != NULL){memset(bit_arr, 0, 32*sizeof(int));for (int index = 0; index < len; ++index){int bit_nums = get_high_bit_one(arr[index]);for (int bit_idx = 0; bit_idx <= bit_nums; ++bit_idx){bit_arr[bit_idx] +=  (arr[index] & (1<<bit_idx))>>bit_idx;}}for (int i = 0; i < 32; ++i){if (bit_arr[i]%3)bit_arr[i] = 1;elsebit_arr[i] = 0;}for (int i = 0; i < 32; ++i){num += bit_arr[i]*(1<<i);}delete[] bit_arr;}return num;}


位操作is coming…

有没有更加简洁的方法呢?我们可以从位操作上入手这个问题。用二进制模拟三级制的运算

B1

B0

Input

B1`

B0`

0

0

1

0

1

0

1

1

1

0

1

0

1

0

0

0

0

0

0

0

0

1

0

0

1

1

0

0

1

0

通过位之间的转换,我们可以得到如下代码.

    int singleNumber(int A[], int n) {        int one = 0, two = 0;        for (int i = 0; i < n; i++) {            int one_ = (one ^ A[i]) & ~two;            int two_ = A[i] & one | ~A[i] & two;            one = one_;            two = two_;        }        return one;    }


另外的方法:

用ones记录到当前计算的变量为止,二进制1出现“1次”(mod 3 之后的 1)的数位。用twos记录到当前计算的变量为止,二进制1出现“2次”(mod 3 之后的 2)的数位。当ones和twos中的某一位同时为1时表示二进制1出现3次,此时需要清零。即用二进制模拟三进制计算。最终ones记录的是最终结果。

int singleNumber(int A[], int n) {    int ones = 0, twos = 0, xthrees = 0;    for(int i = 0; i < n; ++i) {        twos |= (ones & A[i]);        ones ^= A[i];        xthrees = ~(ones & twos);        ones &= xthrees;        twos &= xthrees;    }    return ones;}


更好的方法,而且可以扩展到多位计算的方法。

int getSingleNumber_v2(int a[], int len){if (a == NULL)return 0;int x0 = ~0, x1 = 0, x2 = 0, tmp;for (int i = 0; i < len; ++i){tmp = x2;x2 = (x1 & a[i]) | (x2 & ~a[i]);        x1 = (x0 & a[i]) | (x1 & ~a[i]);        x0 = (tmp & a[i]) | (x0 & ~a[i]);}return x1;}

利用上述方法可以解决这个扩展问题:

Q:Given an array ofintegers, every element appears k times except for one. Find that single one who appears l times.

int getSingleNumber_v3(int A[], int len, int k, int l){    if (A == NULL) return 0;    int t;    int* x = new int[k];    memset(x, 0, k*sizeof(int));    x[0] = ~0;    for (int i = 0; i < len; i++) {        t = x[k-1];        for (int j = k-1; j > 0; j--) {            x[j] = (x[j-1] & A[i]) | (x[j] & ~A[i]);        }        x[0] = (t & A[i]) | (x[0] & ~A[i]);    }    return x[l];}

0 0