位运算的介绍和应用

概要:

1、常见的位运算及距离

2、用异或运算交换两个数

3、某大厂面试题(***)


1、常见的位运算及举例:

2、异或运算交换两个数

扫描二维码关注公众号,回复: 5716232 查看本文章

3、问题描述:

(1)有101个整数,其中有50个数出现了两次,1个数出现了一次, 找出出现了一次的那个数。

(2)有102个整数,其中有50个数出现了两次,2个数出现了一次, 找出出现了一次的那2个数。

(3)有103个整数,其中有50个数出现了两次,3个数出现了一次, 找出出现了一次的那3个数。

要求:不能开数组,即原地找出结果

 分析:主要用异或运算来解决,首先要知道异或的三个性质:

(1)任何数和0异或结果是本身

(2)任何数和自己异或结果是0

(3)异或运算有交换律

根据这几个特点容易解出第一题:将101个数异或,其结果就是要求的那个数。

int get_xor(int array[], int size)
{
    //int array[SIZE] = { 1, 2, 3, 4, 5, 6,1, 2, 3, 4, 5 };
    int xor = 0;
    for (int idx = 0; idx != size; ++idx) {
        xor ^= array[idx];
    }
    //printf("唯一的数为: %d\n", xor);
    return xor;
}

接下来看第二小问:由于有两个不同的数,我们想办法把所有数分成两堆,两个不同的数分别在两个堆中,再运用第一问方法求出两个不同的数即可。

分堆方法:因为有两个不同的数,所有数异或结果必有一位是1,我们找最低位1用来分堆,先找到xor的二进制位中最低位为1的位,再根据该位将数组中的数据分成两类,

其中一类该位为1,另一类该位为0

void find2(int array[], int size) 
{
    int xor = get_xor(array, SIZE + 1);

    //int mask = 1;     //方法一  O(n)
    //while (!(xor & mask))
    //    mask <<= 1;

    int mask = xor & (~xor +1);//方法二 O(1)  
    int number1 = 0, number2 = 0;
    for (int idx = 0; idx != SIZE + 1; ++idx) {
        if (array[idx] & mask) {
            number1 ^= array[idx];
        } else
            number2 ^= array[idx];
    }
    printf("number1 = %d, number2 = %d\n", number1, number2);
}

接下来看第三问,有三个不同的数,如果直接全部异或,必定不行,因为三个不同的数异或,不像两位异或一样必有一位是1,其结果可能有多种。

思想:有三个不同的数,其异或结果可能为0、1,所以先把所有数分成两堆。
目的:将3个不同的数,分到一个奇数堆和一个偶数堆,这样再利用(1)、(2)的结果就可以区分出三个数。
方法:用不断左移的1去和所有数异或。如果是1分到第1堆,如果是0分到第二堆。直到分堆成功(因为有可能所有数的某位都一样)。


注意有可能一个1会把三个数分到同一堆,需要对偶数堆进行异或,若结果为0则说明分堆失败。

#include <stdio.h>

#define SIZE 11

int get_xor(int array[], int size)
{
    //int array[SIZE] = { 1, 2, 3, 4, 5, 6,1, 2, 3, 4, 5 };
    int xor = 0;
    for (int idx = 0; idx != size; ++idx) {
        xor ^= array[idx];
    }
    //printf("唯一的数为: %d\n", xor);
    return xor;
}

void find2(int array[], int size) 
{
    int xor = get_xor(array, SIZE + 1);

    //int mask = 1;
    //while (!(xor & mask))
    //    mask <<= 1;

    int mask = xor & (~xor +1);//O(1)
    int number1 = 0, number2 = 0;
    for (int idx = 0; idx != SIZE + 1; ++idx) {
        if (array[idx] & mask) {
            number1 ^= array[idx];
        } else
            number2 ^= array[idx];
    }
    printf("number1 = %d, number2 = %d\n", number1, number2);
}


//思想:有三个不同的数,其异或结果可能为0、1,所以先把所有数分成两堆。
//目的:将3个不同的数,分到一个奇数堆和一个偶数堆,这样再利用(1)、(2)的结果就可以区分出三个数。
//方法:用不断左移的1去和所有数异或。如果是1分到第1堆,如果是0分到第二堆。直到分堆成功(因为有可能所有数的某位都一样)
//注意:有可能一个1会把三个数分到同一堆,需要对偶数堆进行异或,若结果为0则说明分堆失败。


void find3()
{
    int array[SIZE + 2] = { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5 , 7, 8}; //
    int heap1[SIZE + 2] = { 0 };
    int heap2[SIZE + 2] = { 0 };
    int count1 = 0, count2 = 0;
    int xor1 = 0, xor2 = 0;
    for (int idx = 0; idx < 32; ++idx) {
        for(int j = 0; j < SIZE + 2; ++j) {
            if (array[j] & (1 << idx)) {
                xor1 ^= array[j];
                heap1[count1] = array[j];
                ++count1;
            } else {
                xor2 ^= array[j];
                heap2[count2] = array[j];
                ++count2;
            }
        }
        if (xor1 == 0 || xor2 == 0)
            continue;

        if (count1 > 0 && count1 % 2 == 0) { //若count1为偶数,则count2为奇数
            printf("第一个数是: %d\n", xor2);
            find2(heap1, count1);
            break;
        } else if(count2 > 0 && count2 % 2 == 0){//若count2为偶数,则count1为奇数
            printf("第一个数是: %d\n", xor1);
            find2(heap2, count2);
            break;
        }
    }
}

int main(void)
{
    //find1();
    //find2();
    find3();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Mered1th/p/10628252.html