利用位操作

来源:互联网 发布:免费票据打印软件 编辑:程序博客网 时间:2024/05/16 19:54
数组中只出现一次的一个数
问题:
数组A中,除了某一个数字x之外,其他数字都出现了三次,而x出现了一次。请给出最快的方法找到x
我们换一个角度来看,如果数组中没有x,那么数组中所有的数字都出现了3次,在二进制上,每位上1的个数肯定也能被3整除。如:
{2,5,2,2,5,5}从二进制看:
2:0010
5:0101
2:0010
2:0010
5:0101
5:0101
二进制第0位有3个1,第1位有3个1,第2位有3个1,第4位有0个1,都可以被3整除。
再对该数组添加任何一个数,如果这个数在二进制的某位上为1都将导致该位上1的个数不能被3整除。因此通过统计二进制上每位1的个数就可以推断出x在该位置上是0还是1了,这样就能计算出x了
C++实现:

#include <iostream>

int FindNumber(int a[], int n)

{

    int bits[32];

    int i, j;

    // 累加数组中所有数字的二进制位,一个int类型占4个字节,故用大小为32的数组表示

    memset(bits, 0, 32 * sizeof(int));

    for (i = 0; i < n; i++)

    for (j = 0; j < 32; j++)

        bits[j] += ((a[i] >> j) & 1);

    // 如果某位上的结果不能被整除,则肯定目标数字在这一位上为

    int result = 0;

    for (j = 0; j < 32; j++)

    if (bits[j] % 3 != 0)

        result += (1 << j);

    return result;

}

int main()

{

    const int max = 10;

    int a[max] = { 2, 3, 1, 2, 3, 4, 1, 2, 3, 1 };

    printf("%d\n", FindNumber(a, max));

    return 0;

}

说明:“<<“ 和 ”>>“分别用于左移和右移,&1表示与”00000000 00000000 00000000 00000001“求与操作。
同理出现4次、5次、n次也类似。

数组中只出现一次的两个数
设两个只出现1次的数字分别为AB,如果能将AB分开到二个数组中,那显然符合“异或”解法的关键点了。因此关键点就是将AB分开到二个数组中由于A,B肯定是不相等的,因此在二进制上必定有一位是不同的。根据这一位是0还是1可以将A,B分开到A组和B组。而这个数组中其它数字要么就属于A组,要么就属于B组。再对A组和B组分别执行“异或”解法就可以得到AB了。而要判断AB在哪一位上不相同,只要根据全部数字的异或结果就可以知道了,这个结果在二进制上为1的位都说明AB在这一位上是不相同的,然后再对所有的数字根据这一位是否为1来分组,这样的话两个相同的数字肯定分到同一组,而A、B分到不同组。

    比如int a[] = {1, 1, 3, 5, 2, 2}

    整个数组异或的结果为3^5 0x0011 ^ 0x0101 = 0x0110

    对0x0110,第1位(由低向高,从0开始)就是1。因此整个数组根据第1位是0还是1分成两组。

    a[0] =1  0x0001  第一组

    a[1] =1  0x0001  第一组

    a[2] =3  0x0011  第二组

    a[3] =5  0x0101  第一组

    a[4] =2  0x0010  第二组

    a[5] =2  0x0010  第二组

    第一组有{1, 1, 5},第二组有{3, 2, 3},明显对这二组分别执行“异或”解法就可以得到53了。

C++实现:

#include <iostream>

void FindTwoNotRepeatNumberInArray(int *aint nint *pN1int *pN2)

{

    int i, j, temp;

    //计算这两个数的异或结果

    temp = 0;

    for (i = 0; i < n; i++)

        temp ^= a[i];

    // 找第一个为1的位

    for (j = 0; j < sizeof(int)* 8; j++)

    if (((temp >> j) & 1) == 1)

        break;

    // j位为1,说明这两个数字在第j位上是不相同的

    // 由此分组即可

    *pN1 = 0, *pN2 = 0;

    for (i = 0; i < n; i++)

    if (((a[i] >> j) & 1) == 0)

        *pN1 ^= a[i];

    else

        *pN2 ^= a[i];

}

void PrintfArray(int a[], int n)

{

    for (int i = 0; i < n; i++)

        printf("%d "a[i]);

    putchar('\n');

}

int main()

{

    const int MAXN = 10;

    int a[MAXN] = { 1, 2, 3, 4, 1, 2, 3, 4, 0, 5 };

    printf("数组为: \n");

    PrintfArray(a, MAXN);

    int nNotRepeatNumber1, nNotRepeatNumber2;

    FindTwoNotRepeatNumberInArray(a, MAXN, &nNotRepeatNumber1, &nNotRepeatNumber2);

    printf("两个不重复的数字分别为: %d %d\n", nNotRepeatNumber1, nNotRepeatNumber2);

    return 0;

}











0 0