每日写题分享--关于位运算

&:对应为有0,则为0,其余为1;

        n & (n - 1 ):去掉右边第一个1。

        n & 1 == 1 则说明概述最右位位1,即n为奇数。

|:对应位有1,则是1,其余为0。

^:一样的位置为0,不一样的位置为1。

        n ^ n = 0;

         0 ^ n = n;

        按位异或^可以用于10以内的加法(因为不会产生进位),如果产生进位,则需要用到按位与&配合,假如a & b = c,我们知道,只有相同位置都是1才为1,二进制加法为两个1则进位。所以c中有1的位都是要进位(左移一位)的。可以用&记录需要进位的数。

 所以加减法可以用&和^的配合来完成。看题

题目描述:

 题目链接

代码实现如下:

//方法一:循环
class Solution {

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

//方法二:递归
class Solution {

    public int add(int a, int b) {
        if (b == 0) return a;
        int c = (a & b) << 1;
        a ^= b;
        return add(a,c);
    }
}

再来看这题,求二进制中1的个数。上面说到,n&(n - 1)可以去掉右边第一个1.可以利用这个特性求解本题。

题目描述:

题目链接 

代码实现如下:

public class Solution {
    public int hammingWeight(int n) {
        int count = 0;
        while (n != 0) {
            n = n & (n - 1);
            count++;
        }
        return count;
    }
}

 下面再来看这题,求数组中数字出现的次数I

题目描述:

 解题思路:

全员进行异或操作即可。考虑异或操作的性质:对于两个操作数的每一位,相同结果为 0,不同结果为 1。那么在计算过程中,成对出现的数字的所有位会两两抵消为 0,最终得到的结果就是那个出现了一次的数字。而本题中出现一次的数有两个。所以所有数异或的结果就是那两个只出现一次的数异或的结果。假定为这个结果为res。

那么这一方法如何扩展到找出两个出现一次的数字呢?

如果我们可以把所有数字分成两组,使得:

1、两个只出现一次的数字在不同的组中;

2、相同的数字会被分到相同的组中。

下面进行分组。首先考虑两个相同的数字一定要分在一组。这个很容易实现,我们只需要固定一个二进制位,只要两个数字在这一位相同,就肯定划分在一组。下面就是要将那两个只出现一次的数字x,y分到两个组中,两边分别异或和,相同的两两抵消,最后两边的异或和就是x,y。接下来就剩一个问题,如何把x,y分到两个组里面去。由于res是所有数字异或和,等同于x,y的异或和,x,y不相同,根据异或的性质,res二进制里面的1都是x,y不相同的地方,我们只要找到res中的任意一个1,这里解题我找的是右边第一位1,这个位置记为div,x,y在位置div上是1,根据异或性质,在这个位置上x,y不一样,一个为0,一个为1。然后在遍历数组依次将数组元素与div按位与&运算。那么x,y在&div是,肯定有一个为0,另一个非0。这样两个数就会被分到两个组中。然后分别求异或和就是答案。

代码实现如下:

class Solution {
    public int[] singleNumbers(int[] nums) {
        int res = 0;
        for (int num : nums) {
            res ^= num;     //两个不同值的异或结果,假设为x,y
        }

        int div= 1;
        while ((div & res) == 0) {  //res里的所有1都是代表x,y不相同的位
            div <<= 1;       //找到res从右数的第一个1(x,y的不相等的位)
        }

        int a = 0,b = 0;        //分组
        for (int num : nums) {
            if ((div & num) == 0) {     //能把x,y分开,因为x,y得到的结果肯定是不同的。仔细理解,有点像负负得正。其次数组其他相同的数字也会被分到相同的组,因为相同的数字得到的结果相同。
                a ^= num;
            } else {
                b ^= num;
            }
        }
        return new int[]{a,b};
    }
}

再来练习最后一道关于位运算的题。数组中数字出现次数II

题目描述:

 这题比上一题其实简单一些,没有那么难理解。只要有思路就好做。首先题目说除了一个数字target外,其他的数字都出现三次,意味着没有target的情况下,各二进制位出现的次数都是3的倍数。统计所有数字的各二进制位中 1的出现次数,并对 3 求余,结果则为只出现一次的数字。

 代码实现如下:

class Solution {
    public int singleNumber(int[] nums) {
        int[] counts = new int[32];     //统计个数
        for (int i = 0;i < nums.length;i++) {
            for (int j = 0;j < 32;j++) {
                counts[j] += (nums[i] >> j & 1) == 1 ? 1 : 0;   //将每个数字右移j位再&1,判断最后一位是不是1,然后加到对应位的counts中。
            }
        }
        int target = 0;        
        for (int i = 31;i >= 0;i--) {       
            target <<= 1;      //从后向前将结果输出,每个位置对应counts中的数字后左移判断下一个位置
            if ((counts[i] % 3) == 1) { //说明target在该位置上是1
                target |= 1;   
            }
        }
        return target;
    }
}

  • 25
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值