【算法篇】3.位运算

位运算

  • 基础公式:x^x=0x&(x-1)(消最右 1)、x&1(奇偶)是核心中的核心;
  • 高频技巧:lowbit 公式(x&-x)、2 的幂判断(x&(x-1)==0)、异或交换数需熟练;
  • 实战场景:唯一数、统计 1 的个数、矩阵区域和(结合位运算)等场景直接套用对应公式。
x ^ 0 = x 任何数异或 0 等于自身 101 ^ 000 = 101
x ^ x = 0 任何数异或自身等于 0(核心!) 101 ^ 101 = 000
x & 0 = 0 任何数与 0 等于 0 101 & 000 = 000
x & x = x 任何数与自身等于自身 101 & 101 = 101

x & 1 = 1 → 奇数

**(x >> k) & 1**-> 获取 x 的第 k 位(从 0 开始,最低位是第 0 位)

  • 统计二进制中 1 的个数、判断某一位是否为 1

x & ~(1 << k)->将 x 的第 k 位设为 0(其他位不变)

  • 取消标记、清除某一位

x & (1 << k) != 0 ->判断 x 的第 k 位是否为 1

  • 状态检查

x & (x - 1)---> 消除 x 的二进制中「最右边的 1」

  • 统计 1 的个数、判断是否为 2 的幂

**x & -x**-> 提取 x 的二进制中「最右边的 1」

  • 树状数组、拆分二进制位 、

x & (x - 1) == 0-> 判断 x 是否为 2 的幂(x>0)

  • 2 的幂数识别

必会:

x: 1010000

x-1: 1001111

x&(x-1) 1000000

常用操作

1. 交换两个数(无需临时变量)

  • 面试常考,无临时变量交换数值(注意:a 和 b 不能指向同一内存地址)
java 复制代码
// 核心:a ^ b ^ b = a,a ^ a ^ b = b
a = a ^ b;
b = a ^ b; // 等价于 (a^b)^b = a
a = a ^ b; // 等价于 (a^b)^a = b

2.找数组中唯一出现一次的数(其他数出现两次)

  • 核心逻辑x ^ x = 00 ^ x = x,两次出现的数异或抵消,最终剩下唯一数。
java 复制代码
int res = 0;
for (int num : nums) res ^= num;
return res;

3. 统计二进制中 1 的个数

java 复制代码
int count = 0;
while (x != 0) {
    x = x & (x - 1); // 消除最右边的1
    count++;
}
return count;

4. 快速计算绝对值(无分支)

java 复制代码
int sign = x >> 31; // 最高位:正数0,负数-1(补码)
int abs = (x ^ sign) - sign;

面试题 01.01. 判定字符是否唯一

java 复制代码
class Solution {
    public boolean isUnique(String astr) {
        int n=astr.length();
        if(n>26) return false;

        int bitmap=0; //只用整型变量(26个长度)
        for(int i=0;i<n;i++){
            int idx=astr.charAt(i)-'a'; //第几个位置
            //取第idx位看看是1还是0
            //1:该字符在 0 表不在
            if(((bitmap>>idx)&1)==1) return false; //存在
            //不存在,就得把第idx位设置为1,添加存在
            else  bitmap=bitmap|(1<<idx);
            //100
            //010
            //110(设置第三位为1)
        }
        return true;
    }
}

268. 丢失的数字

利用这两个公式:x^x=0 0^x=x

  • 自己补0-n的数出来
java 复制代码
class Solution {
    public int missingNumber(int[] nums) {
        // x^x=0  0^x=x
        int n=nums.length;
        int ret=0;
        //[0,n-1]
        for(int i=0;i<n;i++){
            ret=ret^nums[i];
            ret=ret^i;
        }
        ret=ret^n; //n
        return ret;

    }
}

136. 只出现一次的数字(同上)

对数组所有元素进行异或^的值就是最终结果。注:两个值不同,则异或结果为1;两个值相同,异或结果为0。

java 复制代码
class Solution {
    public int singleNumber(int[] nums) {
        // x^x=0  0^x=x
        int n=nums.length;
        int ret=0;
        //[0,n-1]
        for(int i=0;i<n;i++){
            ret=ret^nums[i];
        }
        return ret;
    }
}

338. 比特位计数

java 复制代码
class Solution {
    public int[] countBits(int n) {
        int[] ret=new int[n+1];

        for(int i=0;i<=n;i++){
            //统计1的个数
            ret[i]=oneCnt(i);
        }
        return ret;
    }
    //统计一个数二进制1的个数
    public int oneCnt(int n){
        //1010
        int cnt=0;
        while(n!=0){
            if((n&1)==1) cnt++;
            n=n>>1;
        }
        return cnt;
    }
}

统计函数修改为这样,更优雅

java 复制代码
public int oneCnt(int n){
        //1010
        int cnt=0;
        while(n!=0){
            n=n&(n-1);//去掉最右边的1
            cnt++;
        }
        return cnt;
    }

最优解法(DP + 位运算,O (n))

利用位运算的规律优化,核心思路是「状态转移」:

核心规律

对于任意整数 i

  • 如果 i偶数i = 2 * (i/2) → 二进制是 i/2 左移 1 位(末尾加 0)→ 1 的个数和 i/2 相同 → dp[i] = dp[i/2]
  • 如果 i奇数i = 2 * (i/2) + 1 → 二进制是 i/2 左移 1 位 + 末尾加 1 → 1 的个数 = i/2 的个数 + 1 → dp[i] = dp[i/2] + 1

简化公式(用位运算替代除法 / 取模):

  • i/2 等价于 i >> 1(右移 1 位);
  • i 是奇数等价于 i & 1 == 1(最后一位是 1);→ 最终状态转移方程:dp[i] = dp[i >> 1] + (i & 1)
java 复制代码
class Solution {
    public int[] countBits(int n) {
        int[] dp = new int[n + 1];
        for (int i = 1; i <= n; i++) { // 0的1的个数是0,从1开始算
            dp[i] = dp[i >> 1] + (i & 1); // 核心公式
        }
        return dp;
    }
}

461. 汉明距离

核心转化(关键一步)

两个数二进制位「不同」→ 对应位异或(^)结果为 1,「相同」则为 0。因此:

  • 第一步:计算 x ^ y(记为 num),得到一个数,其二进制中「1 的位置」就是 x 和 y 不同的位;
  • 第二步:统计 **num** 中 1 的个数,就是汉明距离
java 复制代码
class Solution {
    public int hammingDistance(int x, int y) {
        //对应位置异或
        //1.不同为1  2.相同为0
        //本质数多少个1
        int num=x^y;
        int cnt=0;
        while(num!=0){
            num=num&(num-1); //去掉最右边1
            cnt++;
        }
        return cnt;
    }
}
相关推荐
無限進步D3 小时前
Java 运行原理
java·开发语言·入门
難釋懷3 小时前
安装Canal
java
是苏浙3 小时前
JDK17新增特性
java·开发语言
不光头强4 小时前
spring cloud知识总结
后端·spring·spring cloud
花酒锄作田5 小时前
企业微信机器人与 DeepAgents 集成实践
python·mcp·deepagents
GetcharZp7 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多7 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood7 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员7 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai
IronMurphy7 小时前
【算法三十九】994. 腐烂的橘子
算法