算法题解(位运算)

剑指 Offer 15. 二进制中1的个数

剑指 Offer 15. 二进制中1的个数

在这里插入图片描述


解析:

在这里插入图片描述
在这里插入图片描述

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0){
            if((n & 1) == 1) res++; //统计1的次数
            n >>>= 1; //无符号右移
        }
        return res;
    }
}

在这里插入图片描述


解法二:

在这里插入图片描述

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0){
            res++; //次数+1
            n &= (n-1); //消去数字 n 最右边的 1}
        return res;
    }
}

在这里插入图片描述

剑指 Offer 65. 不用加减乘除做加法

剑指 Offer 65. 不用加减乘除做加法
在这里插入图片描述


解析:

在这里插入图片描述
在这里插入图片描述

参考:不用加减乘除做加法(位运算,清晰图解)

class Solution {
    public int add(int a, int b) {
        while(b != 0){ //当进位为0时跳出
            int c = (a & b) << 1;  //c=进位
            a ^= b; // a = 非进位和
            b = c; // b = 进位
        }
        return a;
    }
}

在这里插入图片描述

剑指 Offer 56 - I. 数组中数字出现的次数

剑指 Offer 56 - I. 数组中数字出现的次数

在这里插入图片描述


解析:

参考:数组中数字出现的次数(位运算,清晰图解)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注释精简版:

class Solution {
    public int[] singleNumbers(int[] nums) {
        int x = 0, y = 0, n = 0, m = 1;
        for(int num : nums)               // 1. 遍历异或
            n ^= num;
        while((n & m) == 0)               // 2. 循环左移,计算 m
            m <<= 1;
        for(int num: nums) {              // 3. 遍历 nums 分组
            if((num & m) != 0) x ^= num;  // 4. 当 num & m != 0
            else y ^= num;                // 4. 当 num & m == 0
        }
        return new int[] {x, y};          // 5. 返回出现一次的数字
    }
}

注释详细版:

class Solution {
    public int[] singleNumbers(int[] nums) {
        //因为相同的数字异或为0,任何数字与0异或结果是其本身。
        //所以遍历异或整个数组最后得到的结果就是两个只出现一次的数字异或的结果:即 z = x ^ y
        int z = 0;  
        for(int i : nums) z ^= i;
        //我们根据异或的性质可以知道:z中至少有一位是1,否则x与y就是相等的。
        //我们通过一个辅助变量m来保存z中哪一位为1.(可能有多个位都为1,我们找到最低位的1即可)。
        //举个例子:z = 10 ^ 2 = 1010 ^ 0010 = 1000,第四位为1.
        //我们将m初始化为1,如果(z & m)的结果等于0说明z的最低为是0
        //我们每次将m左移一位然后跟z做与操作,直到结果不为0.
        //此时m应该等于1000,同z一样,第四位为1.
        int m = 1;
        while((z & m) == 0) m <<= 1;
        //我们遍历数组,将每个数跟m进行与操作,结果为0的作为一组,结果不为0的作为一组
        //例如对于数组:[1,2,10,4,1,4,3,3],我们把每个数字跟1000做与操作,可以分为下面两组:
        //nums1存放结果为0的: [1, 2, 4, 1, 4, 3, 3]
        //nums2存放结果不为0的: [10] (碰巧nums2中只有一个10,如果原数组中的数字再大一些就不会这样了)
        //此时我们发现问题已经退化为数组中有一个数字只出现了一次
        //分别对nums1和nums2遍历异或就能得到我们预期的x和y
        int x = 0, y = 0;
        for(int i : nums) {
            //这里我们是通过if...else将nums分为了两组,一边遍历一遍异或。
            //跟我们创建俩数组nums1和nums2原理是一样的。
            if((i & m) == 0) x ^= i;
            else y ^= i;
        }
        return new int[]{x, y};
    }
}

在这里插入图片描述

剑指 Offer 56 - II. 数组中数字出现的次数 II

剑指 Offer 56 - II. 数组中数字出现的次数 II

在这里插入图片描述


解法一:用map存数字和布尔值, 存过的数字 布尔值都是true, 只有那个出现一次的数的布尔值是false,找出来就行了

class Solution {
    public int singleNumber(int[] nums) {
        Map<Integer, Boolean> map = new HashMap<>();
        for(Integer num : nums){
            //如果已经存在,就存true,否则为false
            map.put(num, map.containsKey(num));
        }
        for(Integer num : nums){ //找出为false的就为出现1次的数
            if(!map.get(num)) return num;
        }
        return -1;
    }
}

在这里插入图片描述


解法二:位运算 - 有限状态自动机

在这里插入图片描述
在这里插入图片描述

class Solution {
    public int singleNumber(int[] nums) {
        int ones = 0, twos = 0;
        for(int num : nums){
            ones = ones ^ num & ~twos;
            twos = twos ^ num & ~ones;
        }
        return ones;
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值