zoukankan      html  css  js  c++  java
  • 几种常见的位运算

    1、判断奇偶数

    如果把一个数n以二进制数的形式表示的话,我们只需要判断最后一个二进制位是1还是0即可。如果是1,则代表奇数,否则为偶数。代码如下:

    if(n & 1 == 1){
        // n是奇数
    }
    

    2、交换两个数

    x = x ^ y; // (1)
    y = x ^ y; // (2)
    x = x ^ y; // (3)
    

    我们都知道两个相同的数异或之后的结果为0,即 n ^ n = 0,并且任何数与0异或之后等于它本身,即 n ^ 0 = n。

    于是我们把(1)中的x代入(2)中的x,有:y = x ^ y = (x ^ y) ^ y = x ^ ( y ^ y) = x ^ 0 = x,这样x的值就赋给了y。

    对于(3),推导如下:x = x ^ y = (x ^ y) ^ x = (x ^ x) ^ y = 0 ^ y = y,这样y的值就赋给了x。

    异或运算支持运算的交换律和结合律。

    3、找出没有重复的数

    给你一组整型数据,这些数据中,其中有一个数只出现了一次,其他的数都出现了两次,让你来找出一个数

    这道题可能很多人会用一个哈希表来存储,每次存储的时候,记录下某个数出现的次数,最后遍历哈希表找出只出现了一次的次数。这种方式的时间复杂度是O(n),空间复杂度也为O(n)了。

    其实这道题也可以进行位运算,,我们可以把这一组整型数全都异或,由于两个相同的数异或的结果为0,一个数与0异或的结果为其自身,所以异或的所得到的结果就为只出现了一次的数。例如这组数据是:1, 2, 3, 4, 5, 1, 2, 3, 4。其中 5 只出现了一次,其他都出现了两次,把他们全部异或一下,结果如下:

    1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 1 ^ 2 ^ 3 ^ 4 = (1 ^ 1) ^ (2 ^ 2) ^ (3 ^ 3) ^ (4 ^ 4) ^ 5 = 0 ^ 0 ^ 0 ^ 0 ^ 5 = 5

    代码如下:

    int find(int[] nums){
        int tmp = nums[0];
        for(int i = 1;i < nums.length; i++)
            tmp ^= arr[i];
        
        return tmp;
    }
    

    4、2的n次方

    求解 2 的 n 次方,并且不能使用系统自带的 pow 函数

    很多人看到这个题可能觉得让n个2相乘就行了,如果这么做的话,时间复杂度为O(n)了。那么如何用位运算来做呢?

    比如n = 13,n的二进制数表示为1101,那么2的13次方可以拆解为:2 ^ 1101 = 2 ^ 0001 * 2 ^ 0100 * 2 ^ 1000。我们可以通过 & 1和 >>1 来逐位读取 1101,为1时将该位代表的乘数累乘到最终结果。最终代码如下:

    int pow(int n) {
        int sum = 1;
        int tmp = 2;
        while(n != 0) {
            if(n & 1 == 1)
                sum *= tmp;
            
            temp *= temp;
            n >>= 1;
        }
        return sum;
    }
    

    5、找出不大于N的最大的2的幂指数

    例如 N = 19,那么转换成二进制就是 00010011(这里为了方便,我采用8位的二进制来表示)。那么我们要找的数就是,把二进制中最左边的 1 保留,后面的 1 全部变为 0。即我们的目标数是 00010000。那么如何获得这个数呢?相应解法如下:

    1、找到最左边的 1,然后把它右边的所有 0 变成 1

    2、把得到的数值加 1,可以得到 00100000即 00011111 + 1 = 00100000。

    3、把 得到的 00100000 向右移动一位,即可得到 00010000,即 00100000 >> 1 = 00010000。

    那么问题来了,第一步中把最左边 1 中后面的 0 转化为 1 该怎么才能获得呢?

    代码如下:

    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    

    就是通过把 n 右移并且做运算即可得到。我解释下吧,我们假设最左边的 1 处于二进制位中的第 k 位(从左往右数),那么把 n 右移一位之后,那么得到的结果中第 k+1 位也必定为 1,然后把 n 与右移后的结果做或运算,那么得到的结果中第 k 和 第 k + 1 位必定是 1;同样的道理,再次把 n 右移两位,那么得到的结果中第 k+2和第 k+3 位必定是 1,然后再次做或运算,那么就能得到第 k, k+1, k+2, k+3 都是 1,如此往复下去….

    最终的代码如下:

    int findN(int n){
        n |= n >> 1;
        n |= n >> 2;
        n |= n >> 4;
        n |= n >> 8 // 整型一般是 32 位,上面我是假设 8 位。
        return (n + 1) >> 1;
    }
    

    这种做法的时间复杂度近似为 O(1)。

  • 相关阅读:
    读取Java文件到byte数组的三种方式
    https://blog.csdn.net/eguid_1/article/category/6270094
    JavaCV 学习(一):JavaCV 初体验
    WebService服务发布与使用(JDK自带WebService)
    SourceTree 免登录跳过初始设置
    Owin password
    IIS并发
    压力测试
    nginx
    消息队列MSMQ
  • 原文地址:https://www.cnblogs.com/kyoner/p/10964181.html
Copyright © 2011-2022 走看看