[java] 位运算

1、常见位运算符

符号描述运算规则
&两个位都为1时,结果才为1
|两个位都为0时,结果才为0
^异或两个位相同为0,相异为1
~取反0变1,1变0
<<左移各二进位全部左移若干位,高位丢弃,低位补0
>>右移各二进位全部右移若干位,对无符号数,高位补0;有符号数,有的编译器补符号位(算术右移),有的补0(逻辑右移)

1.1 与运算符 &

运算规则: 0&0=0 0&1=0 1&0=0 1&1=1

总结: 两位同时为1,结果才为1,否则结果为0。

注意: 负数按补码形式参加按位与运算。

用途:

  • 清零
    如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

  • 取一个数的指定位
    比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。

  • 判断奇偶
    只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。

1.2 或运算符 |

运算规则: 0|0=0 0|1=1 1|0=1 1|1=1

总结: 参加运算的两个对象只要有一个为1,其值为1。

注意: 负数按补码形式参加按位或运算。

用途: 常用来对一个数据的某些位设置为1
比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。

1.3 异或运算符 ^

运算规则: 0^0=0 0^1=1 1^0=1 1^1=0

总结: 参加运算的两个对象,如果两个相应位相同为0,相异为1。

性质:

  1. 交换律

  2. 结合律 (a ^ b) ^ c == a ^ (b ^ c)

  3. 对于任何数x,都有 x ^ x=0,x ^ 0=x

  4. 自反性: a ^ b ^ b = a ^ 0 = a;

用途:

  • 翻转指定位
    比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。

  • 与0相异或,值不变
    例如:1010 1110 ^ 0000 0000 = 1010 1110

  • 交换两个数

void Swap(int &a, int &b){
    if (a != b){
        a ^= b;
        b ^= a;
        a ^= b;
    }
}

1.4 取反运算符 ~

运算规则: ~1=0 ~0=1

总结: 对一个二进制数按位取反,即将0变1,1变0。

用途: 使一个数的最低位为零
使a的最低位为0,可以表示为:a & ~1。 ~1的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为0。因为“ ~”运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。

1.5 左移运算符 <<

定义: 将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

设 a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。

若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。

1.6 右移运算符 >>

定义: 将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。

操作数每右移一位,相当于该数除以2。

2、应用

2.1 求和

题目

写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。

思路

用十进制做个比喻,计算十进制13+9:

  1. 计算不进位的和。十位1不变,个位3加9等于2,结果为12;

  2. 计算进位。十位没进位,个位进位为1,结果为10。

  3. 再计算十进制12+10:

    1. 计算不进位的和。十位1加1等于2,个位2加0等于2,结果为22;
    2. 计算进位。十位没进位,个位也没进位,结果为0。
  4. 因此结果13+9=22。

在二进制里

  • 求不进位的和可以用异或运算,相同为0,不同为1
  • 求进位可以用与运算,都为1才为1

代码

class Solution {
    public int add(int a, int b) {
        while(b!=0){
            int array=(a&b)<<1;
            a=a^b;
            b=array;
        }
        return a;
    }
}

2.2 只出现一次的数字

题目

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

  • 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1

思路

根据异或运算性质。

  • 对于任何数x,都有 x ^ x=0,x ^ 0=x

  • 自反性: a ^ b ^ b = a ^ 0 = a;

因此,数组中的全部元素的异或运算结果即为数组中只出现一次的数字。

代码

class Solution {
    public int singleNumber(int[] nums) {
        int ans=0;
        for(int num:nums){
            ans^=num;
        }
        return ans;
    }
}

2.3 求子集

题目: 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明: 解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

思路

假设nums=[1,2,3,4],二进制的0可以写成0000,代表一个数也不取,1=0001表示去第一个数也就是[1],2=0010,表示取第二个数[2],3=0011表示取1和2位[1,2],4=0100表示[3]…15=1111表示[1,2,3,4]

代码

    public static List<List<Integer>> binaryBit(int[] nums) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        for (int i = 0; i < (1 << nums.length); i++) {
            List<Integer> sub = new ArrayList<Integer>();
            for (int j = 0; j < nums.length; j++)
                if (((i >> j) & 1) == 1) sub.add(nums[j]);
            res.add(sub);
        }
        return res;
    }

2.4 求幂

题目

实现 pow(x, n) ,即计算 x 的 n 次幂函数。

要求

  • -100.0 < x < 100.0
  • n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。

思路

  1. 将n转换为二进制
    以 x 的 10 次方举例。10 的 2 进制是 1010, x 10 = x ( 1010 ) 2 x^{10}=x^{{(1010)}_2} x10=x(1010)2

  2. 用 2 进制转 10 进制的方法把它展成 2 的幂次的和
    x ( 1010 ) 2 = x 1 ∗ 2 3 + 0 ∗ 2 2 + 1 ∗ 2 1 + 0 ∗ 2 0 x^{{(1010)}_2}=x^{1*2^3+0*2^2+1*2^1+0*2^0} x(1010)2=x123+022+121+020

  3. 把n 的二进制表示中1的位置拿出来
    比如 n 的第 i 位为 1,那么就将 x i x^i xi 拿出来。 x 10 = x 1 ∗ 2 3 ∗ x 1 ∗ 2 1 x^{10}=x^{1*2^3} *x^{1*2^1} x10=x123x121

代码

class Solution {
    public double myPow(double x, long n) {
        if(n<0) return 1.0/myPow(x,-n);
        double res=1;
        while(n!=0){
            if((n&1)==1) res*=x;
            x*=x;
            n>>=1;
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值