阿翰 剑指offer 之 Day 21 位运算 简单

本文介绍了如何利用位运算高效地计算二进制中1的个数,包括逐位判断、巧妙使用n&(n-1)、分治策略以及调用库函数的方法。此外,还探讨了不用加减乘除进行加法运算的迭代加异或与运算和递归策略。这些技巧在计算机科学中有着广泛的应用。
摘要由CSDN通过智能技术生成

目录

位运算

1 二进制中1的个数

1. 逐位判断

2. 巧用 n & (n−1)

3.  分治bit求和

4. 调库

2 不用加减乘除做加法

1. 迭代+异或+与运算

2. 递归


位运算

1 二进制中1的个数

剑指 Offer 15. 二进制中1的个数icon-default.png?t=LA92https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/

1. 逐位判断

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int num = 0; 
        while (n != 0){  
            num += n & 1; 
            n >>>= 1;
        }
        return num;
    }
}

">>>"无符号右移 (逻辑右移)

操作规则:无论正负数,前面补零。

">>"右移

操作规则:正数前面补零,负数前面补1

"<<"左移 (向左移动n位,就相当于乘以2^n)

操作规则:无论正负数,后面补零。 

左移没有<<<运算符

2. 巧用 n & (n−1)

这个运算: n & (n−1),其预算结果恰为把 n 的二进制位中的最低位的 1 变为 0 之后的结果。

 

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

复杂度分析:

  • 时间复杂度 O(M) :n&(n−1) 操作仅有减法和与运算,占用 O(1) ;设 M 为二进制数字 n 中 1 的个数,则需循环 M 次(每轮消去一个 1 ),占用 O(M) 。
  • 空间复杂度 O(1) : 变量 res 使用常数大小额外空间。

3.  分治bit求和

  • 分治思想,先两两位的计算1的数量,再将原来数值的二进制数中的每两位都改写为这两位中1的数量的二进制
  • 代码先一一分组计算二进制1的个数,再两两分组,再四四分组,以此类推直到算出整个32int中的bitCount

​​​参与运算的二进制含义

​0x55555555 = 0101 0101 0101 0101 ...
0x33333333 = 0011 0011 0011 0011 ...
0x0f0f0f0f = 0000 1111 0000 1111 ...
0x3f = 0011 1111

算法流程 

  • 行一将i两两分组,每组为这两位中二进制1的个数
    • 按照原理,这行代码也可改写为(i & 0x55555555) + ((i >>> 1) & 0x55555555)
  • 行二将i四四分组,每组为这四位中二进制1的个数
  • 行三将i八八分组,每组为这八位中二进制1的个数
  • 行四将i按十六位分组,每组为这十六位中二进制1的个数,这里也分为了4组,其中2组为垃圾数据
  • 行四将i按三十二位分组,每组为这三十二位中二进制1的个数,这里也分为了4组,其中3组为垃圾数据
  • 行五取出行四运算结果中的有效数据,6位二进制足够表示最大的32int中的bitCount
public static int bitCount(int i) {
    // HD, Figure 5-2
    i = i - ((i >>> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
    i = (i + (i >>> 4)) & 0x0f0f0f0f;
    i = i + (i >>> 8);
    i = i + (i >>> 16);
    return i & 0x3f;
}

bitCount源码解析 - 为什么不问问神奇海螺呢? 

4. 调库

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        return Integer.bitCount(n);
    }
}

 

2 不用加减乘除做加法

剑指 Offer 65. 不用加减乘除做加法icon-default.png?t=LA92https://leetcode-cn.com/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof/

1. 迭代+异或+与运算

设两数字的二进制形式 a, b ,其求和 s = a + b,a(i) 代表 a 的二进制第 i 位,则分为以下四种情况:

观察发现,无进位和 与 异或运算 规律相同,进位 和 与运算 规律相同(并需左移一位)。因此,无进位和 n 与进位 c 的计算公式如下; 

循环求 n 和 c ,直至进位 c = 0;此时 s = n,返回 n 即可。 

 

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;
    }

复杂度分析:

  • 时间复杂度 O(1): 最差情况下(例如 a = 0x7fffffff , b = 1 时),需循环 32 次,使用 O(1)时间;每轮中的常数次位操作使用 O(1) 时间。
  • 空间复杂度 O(1) : 使用常数大小的额外空间。

Q : 若数字 a 和 b 中有负数,则变成了减法,如何处理?
A : 在计算机系统中,数值一律用 补码 来表示和存储。补码的优势: 加法、减法可以统一处理(CPU只有加法器)。因此,以上方法 同时适用于正数和负数的加法 。

2. 递归

class Solution {
    public int add(int a, int b) {
        if (b == 0) {
            return a;
        }
        // 转换成非进位和 + 进位
        return add(a ^ b, (a & b) << 1);
    }
}

 大佬们的思路一个比一个清晰啊 ,惭愧我一开始还逐位用布尔值保存到栈里,,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值