美文网首页java
位运算指南

位运算指南

作者: coder_girl | 来源:发表于2019-08-18 11:43 被阅读0次

1、判断奇偶数
普通方法:

if(( n % 2) == 1)
    // n 是个奇数
}

实际上 n % 2 的形式,编译器也会自动帮我们优化成位运算
所以可直接使用位运算

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

2、交换两个数
使用一个额外来变量来辅助交换,例如,我们要交换 x 与 y 值,传统代码如下:

int tmp = x;
x = y;
y = tmp;

不允许你使用额外的辅助变量来完成交换呢?
这个时候,位运算大法就来了。代码如下:

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 = (xy)y = x(yy) = x^0 = x。 x 的值成功赋给了 y。

对于(3),推导如下:

x = x^y = (xy)x = (xx)y = 0^y = y。

这里解释一下,异或运算支持运算的交换律和结合律哦。
3、找出没有重复的数
例题:
给你一组整型数据,这些数据中,其中有一个数只出现了一次,其他的数都出现了两次,让你来找出一个数 。
可用一个哈希表来存储,每次存储的时候,记录 某个数出现的次数,最后再遍历哈希表,看看哪个数只出现了一次。这种方法的时间复杂度为 O(n),空间复杂度也为 O(n)了。
位运算时:
两个相同的数异或的结果是 0,一个数和 0 异或的结果是它本身,所以我们把这一组整型全部异或一下,例如这组数据是:1, 2, 3, 4, 5, 1, 2, 3, 4。其中 5 只出现了一次,其他都出现了两次,把他们全部异或一下,结果如下:

由于异或支持交换律和结合律,所以:

123451234 = (11)(22)(33)(44)5= 00005 = 5。

也就是说,那些出现了两次的数异或之后会变成0,那个出现一次的数,和 0 异或之后就等于它本身。所以代码如下:

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

时间复杂度为 O(n),空间复杂度为 O(1),而且看起来很牛逼。

4、m的n次方
如果让你求解 m 的 n 次方,并且不能使用系统自带的 pow 函数,你会怎么做呢?这还不简单,连续让 n 个 m 相乘就行了,代码如下:

int pow(int n){
    int tmp = 1;
    for(int i = 1; i <= n; i++) {
        tmp = tmp * m;
    }
    return tmp;
}

时间复杂度为 O(n)
然而位运算
可以通过 & 1和 >>1 来逐位读取 1101,为1时将该位代表的乘数累乘到最终结果。直接看代码吧,反而容易理解:

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

    return sum;
}

时间复杂度近为 O(logn),而且看起来很牛逼。

这里说一下,位运算很多情况下都是很二进制扯上关系的,所以我们要判断是否是否位运算,很多情况下都会把他们拆分成二进制,然后观察特性,或者就是利用与,或,异或的特性来观察,总之,我觉得多看一些例子,加上自己多动手,就比较容易上手了。所以呢,继续往下看,注意,先别看答案,先看看自己会不会做。
5、找出不大于N的最大的2的幂指数
传统的做法就是让 1 不断着乘以 2,代码如下:

int findN(int N){
    int sum = 1;
   while(true){
        if(sum * 2 > N){
            return sum;
        }
        sum = sum * 2;
   }
}

这样做的话,时间复杂度是 O(logn),那如果改成位运算,该怎么做呢?我刚才说了,如果要弄成位运算的方式,很多时候我们把某个数拆成二进制,然后看看有哪些发现。这里我举个例子吧。

例如 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 该怎么弄呢?我先给出代码再解释吧。下面这段代码就可以把最左边 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),重点是,高逼格。

相关文章

  • 位运算指南

    1、判断奇偶数普通方法: 实际上 n % 2 的形式,编译器也会自动帮我们优化成位运算所以可直接使用位运算 2、交...

  • 面试知识汇总-2019.7.16

    手撕代码题: 其他数据结构与算法中有那些奇技淫巧位运算装逼指南 ---- 带你领略位运算的魅力 单项列表实现加法运...

  • 算法竞赛进阶指南-位运算

    很久没更新简书了,今日回归,要是还有小伙伴能看见,请监督我,我越来越菜了QAQ! a^b题目链接https://a...

  • 3、小众运算符の大课堂(一)

    较为简单の位运算符: & 位与运算| 位或运算^ 位异或运算~ 位取反运算 举例: 要做位运算,首先要把数据转...

  • 位运算及其应用

    内容概要: 位运算基本操作 基于位运算的状态压缩 位运算经典应用 位运算解N皇后问题 位运算 符号描述规则&与1&...

  • 位运算及用位运算实现权限控制

    请自行补习位运算相关知识 位运算 位运算示例 权限控制

  • 开发基础随笔之位运算符(Bitwise Operators)

    位运算符,属于算术运算符 按位逻辑运算符: 位移运算符: 位运算符的运算数只能是整数 位移运算符:按位左移 a<<...

  • 强大的位运算符

    位取反运算符 位取反运算符(~)是对所有位的数字进行取反操作位取反运算符.png 位与运算符 位与运算符(&)可以...

  • 位运算

    位运算 1. &:按位与 规律:一假则假任何位上的数和1相&得到的结果还是那个数 2. |:按位或 规律:一真则真...

  • 位运算

    https://leetcode.com/problems/gray-code/description/这个位运算...

网友评论

    本文标题:位运算指南

    本文链接:https://www.haomeiwen.com/subject/qreksctx.html